diff --git a/README.md b/README.md index 4237d37..dbf7dc8 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ You can find examples of using the SDK under the [examples/](examples/) director **Note: Only Twitter API V2 is supported** -- API version: 2.45 +- API version: 2.46 Twitter API v2 available endpoints @@ -228,8 +228,8 @@ In order to use the retry mechanism call the APIs with an additional parameter ` ```java int retries = 4; - streamResult = apiInstance.tweets()apiInstance.tweets().sampleStream() - .tweetFields(tweetFields) + streamResult = apiInstance.tweets().sampleStream() + .parameters(new StreamQueryParameters.Builder.withTweetFields(tweetFields).build()) .execute(retries); ``` @@ -530,6 +530,13 @@ Class | Method | HTTP request | Description - [VideoAllOfPromotedMetrics](docs/VideoAllOfPromotedMetrics.md) - [VideoAllOfPublicMetrics](docs/VideoAllOfPublicMetrics.md) +## Documentation for Stream Functionality + + +Class | Method | HTTP request | Description +------------ |--------------------------------------------------------| ------------- | ------------- +*TwitterStream* | [**sampleStream**](docs/TwitterStream.md#sampleStream) | **GET** /2/tweets/sample/stream | Processing stream with multiple threads accepts listeners with the produced tweets + diff --git a/docs/StreamQueryParameters.md b/docs/StreamQueryParameters.md new file mode 100644 index 0000000..a9921df --- /dev/null +++ b/docs/StreamQueryParameters.md @@ -0,0 +1,114 @@ + + +# StreamQueryParameters + + +## Properties + + +| Name | Type | Description | Notes | +|------------- |----------------------------------------------| ------------- | -------------| +| **backfillMinutes** | **Integer** | The number of minutes of backfill requested. | [optional] | +| **tweetFields** | [**Collection<TweetField>**](#TweetField) | A comma separated list of Tweet fields to display. | [optional] [enum: attachments, author_id, context_annotations, conversation_id, created_at, entities, geo, id, in_reply_to_user_id, lang, non_public_metrics, organic_metrics, possibly_sensitive, promoted_metrics, public_metrics, referenced_tweets, reply_settings, source, text, withheld] | +| **expansions** | [**Collection<Expansion>**](#Expansion) | A comma separated list of fields to expand. | [optional] [enum: attachments.media_keys, attachments.poll_ids, author_id, entities.mentions.username, geo.place_id, in_reply_to_user_id, referenced_tweets.id, referenced_tweets.id.author_id] | +| **mediaFields** | [**Collection<MediaField>**](#MediaField) | A comma separated list of Media fields to display. | [optional] [enum: alt_text, duration_ms, height, media_key, non_public_metrics, organic_metrics, preview_image_url, promoted_metrics, public_metrics, type, url, variants, width] | +| **pollFields** | [**Collection<PollField>**](#PollField) | A comma separated list of Poll fields to display. | [optional] [enum: duration_minutes, end_datetime, id, options, voting_status] | +| **userFields** | [**Collection<UserField>**](#UserField) | A comma separated list of User fields to display. | [optional] [enum: created_at, description, entities, id, location, name, pinned_tweet_id, profile_image_url, protected, public_metrics, url, username, verified, withheld] | +| **placeFields** | [**Collection<PlaceField>**](#PlaceField) | A comma separated list of Place fields to display. | [optional] [enum: contained_within, country, country_code, full_name, geo, id, name, place_type] | + + + + +## Enum: TweetField + +| Name | Value | +|---- | -----| +|ATTACHMENTS|"attachments"| +|AUTHOR_ID|"author_id"| +|CONTEXT_ANNOTATIONS|"context_annotations"| +|CONVERSATION_ID|"conversation_id"| +|CREATED_AT|"created_at"| +|ENTITIES|"entities"| +|GEO|"geo"| +|ID|"id"| +|IN_REPLY_TO_USER_ID|"in_reply_to_user_id"| +|LANG|"lang"| +|POSSIBLE_SENSITIVE|"possibly_sensitive"| +|PUBLIC_METRICS|"public_metrics"| +|REFERENCED_TWEETS|"referenced_tweets"| +|REPLY_SETTINGS|"reply_settings"| +|SOURCE|"source"| +|TEXT|"text"| +|WITHHELD|"withheld"| + + +## Enum: Expansion + +| Name | Value | +|---- | -----| +|ATTACHMENTS_MEDIA_KEYS|"attachments.media_keys"| +|ATTACHMENTS_POLL_IDS|"attachments.poll_ids"| +|AUTHOR_ID|"author_id"| +|ENTITIES_MENTIONS_USERNAME|"entities.mentions.username"| +|GEO_PLACE_ID|"geo.place_id"| +|IN_REPLY_TO_USER_ID|"in_reply_to_user_id"| +|REFERENCED_TWEETS_ID|"referenced_tweets.id"| +|REFERENCED_TWEETS_ID_AUTHOR_ID|"referenced_tweets.id.author_id"| + +## Enum: MediaField + +| Name | Value | +|---- | -----| +|ALT_TEXT|"alt_text"| +|DURATION_MS|"duration_ms"| +|HEIGHT|"height"| +|MEDIA_KEY|"media_key"| +|PREVIEW_IMAGE_URL|"preview_image_url"| +|PUBLIC_METRICS|"public_metrics"| +|TYPE|"type"| +|URL|"url"| +|VARIANTS|"variants"| +|WIDTH|"width"| + + +## Enum: PollField + +| Name | Value | +|---- | -----| +|DURATION_MINUTES|"duration_minutes"| +|END_DATETIME|"end_datetime"| +|ID|"id"| +|OPTIONS|"options"| +|VOTING_STATUS|"voting_status"| + +## Enum: UserField + +| Name | Value | +|---- | -----| +|CREATED_AT|"created_at"| +|DESCRIPTION|"description"| +|ENTITIES|"entities"| +|ID|"id"| +|LOCATION|"location"| +|NAME|"name"| +|PINNED_TWEET_ID|"pinned_tweet_id"| +|PROFILE_IMAGE_URL|"profile_image_url"| +|PROTECTED|"protected"| +|PUBLIC_METRICS|"public_metrics"| +|URL|"url"| +|USERNAME|"username"| +|VERIFIED|"verified"| +|WITHHELD|"withheld"| + +## Enum: PlaceField + +| Name | Value | +|---- | -----| +|CONTAINED_WITHIN|"contained_within"| +|COUNTRY|"country"| +|COUNTRY_CODE|"country_code"| +|FULL_NAME|"full_name"| +|GEO|"geo"| +|ID|"id"| +|NAME|"name"| +|PLACE_TYPE|"place_type"| \ No newline at end of file diff --git a/docs/TweetsApi.md b/docs/TweetsApi.md index bfec075..133e967 100644 --- a/docs/TweetsApi.md +++ b/docs/TweetsApi.md @@ -895,15 +895,29 @@ public class Example { # **sampleStream** -> StreamingTweetResponse sampleStream().backfillMinutes(backfillMinutes).tweetFields(tweetFields).expansions(expansions).mediaFields(mediaFields).pollFields(pollFields).userFields(userFields).placeFields(placeFields).execute(); +```java +BufferedSource sampleStream() + .parameters(new StreamQueryParameters.Builder() + .withBackfillMinutes(backfillMinutes) + .withTweetFields(tweetFields) + .withExpansions(expansions) + .withMediaFields(mediaFields) + .withPollFields(pollFields) + .withUserFields(userFields) + .withPlaceFields(placeFields) + .build()) + .execute(); +``` Sample stream Streams a deterministic 1% of public Tweets. ### Example + ```java // Import classes: + import com.twitter.clientlib.ApiClient; import com.twitter.clientlib.ApiException; import com.twitter.clientlib.Configuration; @@ -914,10 +928,15 @@ import com.twitter.clientlib.TwitterCredentialsBearer; import com.twitter.clientlib.api.TwitterApi; import com.twitter.clientlib.api.TweetsApi; + import java.io.InputStream; + import com.google.common.reflect.TypeToken; -import java.io.BufferedReader; -import java.io.InputStreamReader; + +import com.twitter.clientlib.query.StreamQueryParameters; +import com.twitter.clientlib.query.model.TweetField; +import okio.BufferedSource; + import java.lang.reflect.Type; import java.util.List; import java.util.Set; @@ -926,76 +945,59 @@ import java.util.HashSet; import java.time.OffsetDateTime; public class Example { - public static void main(String[] args) { - // Set the credentials based on the API's "security" tag values. - // Check the API definition in https://api.twitter.com/2/openapi.json - // When multiple options exist, the SDK supports only "OAuth2UserToken" or "BearerToken" + public static void main(String[] args) { + // Set the credentials based on the API's "security" tag values. + // Check the API definition in https://api.twitter.com/2/openapi.json + // When multiple options exist, the SDK supports only "OAuth2UserToken" or "BearerToken" - // Uncomment and set the credentials configuration - - // Configure HTTP bearer authorization: - // TwitterCredentialsBearer credentials = new TwitterCredentialsBearer(System.getenv("TWITTER_BEARER_TOKEN")); + // Uncomment and set the credentials configuration + + // Configure HTTP bearer authorization: + // TwitterCredentialsBearer credentials = new TwitterCredentialsBearer(System.getenv("TWITTER_BEARER_TOKEN")); TwitterApi apiInstance = new TwitterApi(credentials); - // Set the params values - Integer backfillMinutes = 56; // Integer | The number of minutes of backfill requested. - Set tweetFields = new HashSet<>(Arrays.asList()); // Set | A comma separated list of Tweet fields to display. - Set expansions = new HashSet<>(Arrays.asList()); // Set | A comma separated list of fields to expand. - Set mediaFields = new HashSet<>(Arrays.asList()); // Set | A comma separated list of Media fields to display. - Set pollFields = new HashSet<>(Arrays.asList()); // Set | A comma separated list of Poll fields to display. - Set userFields = new HashSet<>(Arrays.asList()); // Set | A comma separated list of User fields to display. - Set placeFields = new HashSet<>(Arrays.asList()); // Set | A comma separated list of Place fields to display. - try { - InputStream result = apiInstance.tweets().sampleStream() - .backfillMinutes(backfillMinutes) - .tweetFields(tweetFields) - .expansions(expansions) - .mediaFields(mediaFields) - .pollFields(pollFields) - .userFields(userFields) - .placeFields(placeFields) - .execute(); - try{ - JSON json = new JSON(); - Type localVarReturnType = new TypeToken(){}.getType(); - BufferedReader reader = new BufferedReader(new InputStreamReader(result)); - String line = reader.readLine(); - while (line != null) { - if(line.isEmpty()) { - System.out.println("==> Empty line"); - line = reader.readLine(); - continue; - } - Object jsonObject = json.getGson().fromJson(line, localVarReturnType); - System.out.println(jsonObject != null ? jsonObject.toString() : "Null object"); - line = reader.readLine(); - } - }catch (Exception e) { - e.printStackTrace(); - System.out.println(e); + try { + BufferedSource result = apiInstance.tweets().sampleStream() + .parameters(new StreamQueryParameters.Builder() + .withBackfillMinutes(56) + .withTweetFields(TweetField.AUTHOR_ID, TweetField.ID, TweetField.CREATED_AT) + .build()) + .execute(); + try { + JSON json = new JSON(); + Type localVarReturnType = new TypeToken() { + }.getType(); + String line = result.readUtf8Line(); + while (line != null) { + if (line.isEmpty()) { + System.out.println("==> Empty line"); + line = result.readUtf8Line(); + continue; + } + Object jsonObject = json.getGson().fromJson(line, localVarReturnType); + System.out.println(jsonObject != null ? jsonObject.toString() : "Null object"); + line = result.readUtf8Line(); + } + } catch (Exception e) { + e.printStackTrace(); + System.out.println(e); } - } catch (ApiException e) { - System.err.println("Exception when calling TweetsApi#sampleStream"); - System.err.println("Status code: " + e.getCode()); - System.err.println("Reason: " + e.getResponseBody()); - System.err.println("Response headers: " + e.getResponseHeaders()); - e.printStackTrace(); + } catch (ApiException e) { + System.err.println("Exception when calling TweetsApi#sampleStream"); + System.err.println("Status code: " + e.getCode()); + System.err.println("Reason: " + e.getResponseBody()); + System.err.println("Response headers: " + e.getResponseHeaders()); + e.printStackTrace(); + } } - } } ``` ### Parameters -| Name | Type | Description | Notes | -|------------- | ------------- | ------------- | -------------| -| **backfillMinutes** | **Integer**| The number of minutes of backfill requested. | [optional] | -| **tweetFields** | [**Set<String>**](String.md)| A comma separated list of Tweet fields to display. | [optional] [enum: attachments, author_id, context_annotations, conversation_id, created_at, entities, geo, id, in_reply_to_user_id, lang, non_public_metrics, organic_metrics, possibly_sensitive, promoted_metrics, public_metrics, referenced_tweets, reply_settings, source, text, withheld] | -| **expansions** | [**Set<String>**](String.md)| A comma separated list of fields to expand. | [optional] [enum: attachments.media_keys, attachments.poll_ids, author_id, entities.mentions.username, geo.place_id, in_reply_to_user_id, referenced_tweets.id, referenced_tweets.id.author_id] | -| **mediaFields** | [**Set<String>**](String.md)| A comma separated list of Media fields to display. | [optional] [enum: alt_text, duration_ms, height, media_key, non_public_metrics, organic_metrics, preview_image_url, promoted_metrics, public_metrics, type, url, variants, width] | -| **pollFields** | [**Set<String>**](String.md)| A comma separated list of Poll fields to display. | [optional] [enum: duration_minutes, end_datetime, id, options, voting_status] | -| **userFields** | [**Set<String>**](String.md)| A comma separated list of User fields to display. | [optional] [enum: created_at, description, entities, id, location, name, pinned_tweet_id, profile_image_url, protected, public_metrics, url, username, verified, withheld] | -| **placeFields** | [**Set<String>**](String.md)| A comma separated list of Place fields to display. | [optional] [enum: contained_within, country, country_code, full_name, geo, id, name, place_type] | +| Name | Type | Description | Notes | +|-------|-----------------------------------------------------------------|------------|-------| +| **parameters** | [**StreamQueryParameters**](StreamQueryParameters.md) | | | ### Return type @@ -1018,15 +1020,29 @@ public class Example { # **searchStream** -> FilteredStreamingTweetResponse searchStream().backfillMinutes(backfillMinutes).tweetFields(tweetFields).expansions(expansions).mediaFields(mediaFields).pollFields(pollFields).userFields(userFields).placeFields(placeFields).execute(); +```java +FilteredStreamingTweetResponse searchStream() + .parameters(new StreamQueryParameters.Builder() + .withBackfillMinutes(backfillMinutes) + .withTweetFields(tweetFields) + .withExpansions(expansions) + .withMediaFields(mediaFields) + .withPollFields(pollFields) + .withUserFields(userFields) + .withPlaceFields(placeFields) + .build()) + .execute(); +``` Filtered stream Streams Tweets matching the stream's active rule set. ### Example + ```java // Import classes: + import com.twitter.clientlib.ApiClient; import com.twitter.clientlib.ApiException; import com.twitter.clientlib.Configuration; @@ -1037,10 +1053,14 @@ import com.twitter.clientlib.TwitterCredentialsBearer; import com.twitter.clientlib.api.TwitterApi; import com.twitter.clientlib.api.TweetsApi; + import java.io.InputStream; + import com.google.common.reflect.TypeToken; -import java.io.BufferedReader; -import java.io.InputStreamReader; +import com.twitter.clientlib.query.StreamQueryParameters; +import com.twitter.clientlib.query.model.TweetField; +import okio.BufferedSource; + import java.lang.reflect.Type; import java.util.List; import java.util.Set; @@ -1049,62 +1069,51 @@ import java.util.HashSet; import java.time.OffsetDateTime; public class Example { - public static void main(String[] args) { - // Set the credentials based on the API's "security" tag values. - // Check the API definition in https://api.twitter.com/2/openapi.json - // When multiple options exist, the SDK supports only "OAuth2UserToken" or "BearerToken" + public static void main(String[] args) { + // Set the credentials based on the API's "security" tag values. + // Check the API definition in https://api.twitter.com/2/openapi.json + // When multiple options exist, the SDK supports only "OAuth2UserToken" or "BearerToken" - // Uncomment and set the credentials configuration - - // Configure HTTP bearer authorization: - // TwitterCredentialsBearer credentials = new TwitterCredentialsBearer(System.getenv("TWITTER_BEARER_TOKEN")); + // Uncomment and set the credentials configuration + + // Configure HTTP bearer authorization: + // TwitterCredentialsBearer credentials = new TwitterCredentialsBearer(System.getenv("TWITTER_BEARER_TOKEN")); TwitterApi apiInstance = new TwitterApi(credentials); - // Set the params values - Integer backfillMinutes = 56; // Integer | The number of minutes of backfill requested. - Set tweetFields = new HashSet<>(Arrays.asList()); // Set | A comma separated list of Tweet fields to display. - Set expansions = new HashSet<>(Arrays.asList()); // Set | A comma separated list of fields to expand. - Set mediaFields = new HashSet<>(Arrays.asList()); // Set | A comma separated list of Media fields to display. - Set pollFields = new HashSet<>(Arrays.asList()); // Set | A comma separated list of Poll fields to display. - Set userFields = new HashSet<>(Arrays.asList()); // Set | A comma separated list of User fields to display. - Set placeFields = new HashSet<>(Arrays.asList()); // Set | A comma separated list of Place fields to display. - try { - InputStream result = apiInstance.tweets().searchStream() - .backfillMinutes(backfillMinutes) - .tweetFields(tweetFields) - .expansions(expansions) - .mediaFields(mediaFields) - .pollFields(pollFields) - .userFields(userFields) - .placeFields(placeFields) - .execute(); - try{ - JSON json = new JSON(); - Type localVarReturnType = new TypeToken(){}.getType(); - BufferedReader reader = new BufferedReader(new InputStreamReader(result)); - String line = reader.readLine(); - while (line != null) { - if(line.isEmpty()) { - System.out.println("==> Empty line"); - line = reader.readLine(); - continue; - } - Object jsonObject = json.getGson().fromJson(line, localVarReturnType); - System.out.println(jsonObject != null ? jsonObject.toString() : "Null object"); - line = reader.readLine(); - } - }catch (Exception e) { - e.printStackTrace(); - System.out.println(e); + try { + BufferedSource result = apiInstance.tweets().searchStream() + .parameters(new StreamQueryParameters.Builder() + .withBackfillMinutes(56) + .withTweetFields(TweetField.AUTHOR_ID, TweetField.ID, TweetField.CREATED_AT) + .build()) + .execute(); + try { + JSON json = new JSON(); + Type localVarReturnType = new TypeToken() { + }.getType(); + String line = result.readUtf8Line(); + while (line != null) { + if (line.isEmpty()) { + System.out.println("==> Empty line"); + line = result.readUtf8Line(); + continue; + } + Object jsonObject = json.getGson().fromJson(line, localVarReturnType); + System.out.println(jsonObject != null ? jsonObject.toString() : "Null object"); + line = result.readUtf8Line(); + } + } catch (Exception e) { + e.printStackTrace(); + System.out.println(e); } - } catch (ApiException e) { - System.err.println("Exception when calling TweetsApi#searchStream"); - System.err.println("Status code: " + e.getCode()); - System.err.println("Reason: " + e.getResponseBody()); - System.err.println("Response headers: " + e.getResponseHeaders()); - e.printStackTrace(); + } catch (ApiException e) { + System.err.println("Exception when calling TweetsApi#searchStream"); + System.err.println("Status code: " + e.getCode()); + System.err.println("Reason: " + e.getResponseBody()); + System.err.println("Response headers: " + e.getResponseHeaders()); + e.printStackTrace(); + } } - } } ``` @@ -1112,13 +1121,7 @@ public class Example { | Name | Type | Description | Notes | |------------- | ------------- | ------------- | -------------| -| **backfillMinutes** | **Integer**| The number of minutes of backfill requested. | [optional] | -| **tweetFields** | [**Set<String>**](String.md)| A comma separated list of Tweet fields to display. | [optional] [enum: attachments, author_id, context_annotations, conversation_id, created_at, entities, geo, id, in_reply_to_user_id, lang, non_public_metrics, organic_metrics, possibly_sensitive, promoted_metrics, public_metrics, referenced_tweets, reply_settings, source, text, withheld] | -| **expansions** | [**Set<String>**](String.md)| A comma separated list of fields to expand. | [optional] [enum: attachments.media_keys, attachments.poll_ids, author_id, entities.mentions.username, geo.place_id, in_reply_to_user_id, referenced_tweets.id, referenced_tweets.id.author_id] | -| **mediaFields** | [**Set<String>**](String.md)| A comma separated list of Media fields to display. | [optional] [enum: alt_text, duration_ms, height, media_key, non_public_metrics, organic_metrics, preview_image_url, promoted_metrics, public_metrics, type, url, variants, width] | -| **pollFields** | [**Set<String>**](String.md)| A comma separated list of Poll fields to display. | [optional] [enum: duration_minutes, end_datetime, id, options, voting_status] | -| **userFields** | [**Set<String>**](String.md)| A comma separated list of User fields to display. | [optional] [enum: created_at, description, entities, id, location, name, pinned_tweet_id, profile_image_url, protected, public_metrics, url, username, verified, withheld] | -| **placeFields** | [**Set<String>**](String.md)| A comma separated list of Place fields to display. | [optional] [enum: contained_within, country, country_code, full_name, geo, id, name, place_type] | +| **parameters** | [**StreamQueryParameters**](StreamQueryParameters.md) | | | ### Return type diff --git a/docs/TwitterStream.md b/docs/TwitterStream.md new file mode 100644 index 0000000..332e75c --- /dev/null +++ b/docs/TwitterStream.md @@ -0,0 +1,63 @@ +# TwitterStream + +A wrapper for calling the stream endpoints reading the BufferedSource and producing deserialized responses from every line of the BufferedSource. + + + +# **sampleStream** + +```java +twitterStream.addListener(new TweetListener()); +twitterStream.startSampleStream(new StreamQueryParameters.Builder().build()); +``` + +The listener must implement this method +```java +void onTweetArrival(StreamingTweetResponse streamingTweet); +``` + +### Example + +```java +import com.twitter.clientlib.model.*; +import com.twitter.clientlib.query.StreamQueryParameters; +import com.twitter.clientlib.query.model.TweetField; +import com.twitter.clientlib.stream.TweetsStreamListener; +import com.twitter.clientlib.stream.TwitterStream; + + +public class HelloWorldStreaming { + + public static void main(String[] args) { + /** + * Set the credentials for the required APIs. + * The Java SDK supports TwitterCredentialsOAuth2 & TwitterCredentialsBearer. + * Check the 'security' tag of the required APIs in https://api.twitter.com/2/openapi.json in order + * to use the right credential object. + */ + TwitterStream twitterStream = new TwitterStream(new TwitterCredentialsBearer(System.getenv("TWITTER_BEARER_TOKEN"))); + twitterStream.addListener(new Responder()); + + twitterStream.startSampleStream(new StreamQueryParameters.Builder() + .withTweetFields(TweetField.AUTHOR_ID, TweetField.ID, TweetField.CREATED_AT) + .build()); + + } +} + +class Responder implements TweetsStreamListener { + @Override + public void onTweetArrival(StreamingTweetResponse streamingTweet) { + if (streamingTweet == null) { + System.err.println("Error: actionOnTweetsStream - streamingTweet is null "); + return; + } + + if (streamingTweet.getErrors() != null) { + streamingTweet.getErrors().forEach(System.out::println); + } else if (streamingTweet.getData() != null) { + System.out.println("New streaming tweet: " + streamingTweet.getData().getText()); + } + } +} +``` diff --git a/examples/pom.xml b/examples/pom.xml index a779e4b..e1867bd 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -19,5 +19,10 @@ 2.0.0 compile + + org.slf4j + slf4j-simple + 1.7.5 + diff --git a/examples/src/main/java/com/twitter/clientlib/HelloWorldStreaming.java b/examples/src/main/java/com/twitter/clientlib/HelloWorldStreaming.java index c9ed12a..61f1f92 100644 --- a/examples/src/main/java/com/twitter/clientlib/HelloWorldStreaming.java +++ b/examples/src/main/java/com/twitter/clientlib/HelloWorldStreaming.java @@ -23,15 +23,11 @@ package com.twitter.clientlib; -import java.util.HashSet; -import java.util.Set; -import java.io.InputStream; - -import com.twitter.clientlib.ApiException; -import com.twitter.clientlib.TwitterCredentialsBearer; -import com.twitter.clientlib.TweetsStreamListenersExecutor; -import com.twitter.clientlib.api.TwitterApi; import com.twitter.clientlib.model.*; +import com.twitter.clientlib.query.StreamQueryParameters; +import com.twitter.clientlib.query.model.TweetField; +import com.twitter.clientlib.stream.TweetsStreamListener; +import com.twitter.clientlib.stream.TwitterStream; public class HelloWorldStreaming { @@ -43,64 +39,19 @@ public static void main(String[] args) { * Check the 'security' tag of the required APIs in https://api.twitter.com/2/openapi.json in order * to use the right credential object. */ - TwitterApi apiInstance = new TwitterApi(new TwitterCredentialsBearer(System.getenv("TWITTER_BEARER_TOKEN"))); - - Set tweetFields = new HashSet<>(); - tweetFields.add("author_id"); - tweetFields.add("id"); - tweetFields.add("created_at"); + TwitterStream twitterStream = new TwitterStream(new TwitterCredentialsBearer(System.getenv("TWITTER_BEARER_TOKEN"))); + twitterStream.addListener(new Responder()); - try { - InputStream streamResult = apiInstance.tweets().sampleStream() - .backfillMinutes(0) - .tweetFields(tweetFields) - .execute(); - // sampleStream with TweetsStreamListenersExecutor - Responder responder = new Responder(); - TweetsStreamListenersExecutor tsle = new TweetsStreamListenersExecutor(streamResult); - tsle.addListener(responder); - tsle.executeListeners(); + twitterStream.startSampleStream(new StreamQueryParameters.Builder() + .withTweetFields(TweetField.AUTHOR_ID, TweetField.ID, TweetField.CREATED_AT) + .build()); -// // Shutdown TweetsStreamListenersExecutor -// try { -// Thread.sleep(20000); -// tsle.shutdown(); -// } catch (InterruptedException e) { -// e.printStackTrace(); -// } - -// // An example of how to use the streaming directly using the InputStream result (Without TweetsStreamListenersExecutor) -// try{ -// JSON json = new JSON(); -// Type localVarReturnType = new TypeToken(){}.getType(); -// BufferedReader reader = new BufferedReader(new InputStreamReader(streamResult)); -// String line = reader.readLine(); -// while (line != null) { -// if(line.isEmpty()) { -// System.err.println("==> " + line.isEmpty()); -// line = reader.readLine(); -// continue; -// } -// Object jsonObject = json.getGson().fromJson(line, localVarReturnType); -// System.out.println(jsonObject != null ? jsonObject.toString() : "Null object"); -// line = reader.readLine(); -// } -// }catch (Exception e) { -// e.printStackTrace(); -// System.out.println(e); -// } - } catch (ApiException e) { - System.err.println("Status code: " + e.getCode()); - System.err.println("Reason: " + e.getResponseBody()); - System.err.println("Response headers: " + e.getResponseHeaders()); - e.printStackTrace(); - } } } -class Responder implements com.twitter.clientlib.TweetsStreamListener { +class Responder implements TweetsStreamListener { @Override - public void actionOnTweetsStream(StreamingTweetResponse streamingTweet) { + public void onTweetArrival(StreamingTweetResponse streamingTweet) { if(streamingTweet == null) { System.err.println("Error: actionOnTweetsStream - streamingTweet is null "); return; diff --git a/examples/src/main/java/com/twitter/clientlib/TweetsStreamListenersExecutor.java b/examples/src/main/java/com/twitter/clientlib/TweetsStreamListenersExecutor.java deleted file mode 100644 index c3e9439..0000000 --- a/examples/src/main/java/com/twitter/clientlib/TweetsStreamListenersExecutor.java +++ /dev/null @@ -1,150 +0,0 @@ -/* -Copyright 2020 Twitter, Inc. -SPDX-License-Identifier: Apache-2.0 - -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. - -NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). -https://openapi-generator.tech -Do not edit the class manually. -*/ - - -package com.twitter.clientlib; - - -import java.io.BufferedReader; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.lang.reflect.Type; -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.Queue; - -import com.google.gson.reflect.TypeToken; - -import com.twitter.clientlib.model.StreamingTweetResponse; - -public class TweetsStreamListenersExecutor { - private final ITweetsQueue tweetsQueue; - private final List listeners = new ArrayList<>(); - private final InputStream stream; - private volatile boolean isRunning = true; - - public TweetsStreamListenersExecutor(InputStream stream) { - this.tweetsQueue = new LinkedListTweetsQueue(); - this.stream = stream; - } - - public TweetsStreamListenersExecutor(ITweetsQueue tweetsQueue, InputStream stream) { - this.tweetsQueue = tweetsQueue; - this.stream = stream; - } - - public void addListener(TweetsStreamListener toAdd) { - listeners.add(toAdd); - } - - public void executeListeners() { - if (stream == null) { - System.out.println("Error: stream is null."); - return; - } else if (this.tweetsQueue == null) { - System.out.println("Error: tweetsQueue is null."); - return; - } - - TweetsQueuer tweetsQueuer = new TweetsQueuer(); - TweetsListenersExecutor tweetsListenersExecutor = new TweetsListenersExecutor(); - tweetsListenersExecutor.start(); - tweetsQueuer.start(); - } - - public synchronized void shutdown() { - isRunning = false; - System.out.println("TweetsStreamListenersExecutor is shutting down."); - } - - private class TweetsListenersExecutor extends Thread { - @Override - public void run() { - processTweets(); - } - - private void processTweets() { - StreamingTweetResponse streamingTweet; - try { - while (isRunning) { - streamingTweet = tweetsQueue.poll(); - if (streamingTweet == null) { - Thread.sleep(100); - continue; - } - for (TweetsStreamListener listener : listeners) { - listener.actionOnTweetsStream(streamingTweet); - } - } - } catch (Exception e) { - e.printStackTrace(); - } - } - } - - private class TweetsQueuer extends Thread { - @Override - public void run() { - queueTweets(); - } - - public void queueTweets() { - String line = null; - try (BufferedReader reader = new BufferedReader(new InputStreamReader(stream))) { - while (isRunning) { - line = reader.readLine(); - if(line == null || line.isEmpty()) { - Thread.sleep(100); - continue; - } - try { - tweetsQueue.add(StreamingTweetResponse.fromJson(line)); - } catch (Exception interExcep) { - interExcep.printStackTrace(); - } - } - } catch (Exception e) { - e.printStackTrace(); - shutdown(); - } - } - } -} - -interface ITweetsQueue { - StreamingTweetResponse poll(); - void add(StreamingTweetResponse streamingTweet); -} - -class LinkedListTweetsQueue implements ITweetsQueue { - private final Queue tweetsQueue = new LinkedList<>(); - - @Override - public StreamingTweetResponse poll() { - return tweetsQueue.poll(); - } - - @Override - public void add(StreamingTweetResponse streamingTweet) { - tweetsQueue.add(streamingTweet); - } -} diff --git a/src/main/java/com/twitter/clientlib/ApiClient.java b/src/main/java/com/twitter/clientlib/ApiClient.java index f682612..c1416eb 100644 --- a/src/main/java/com/twitter/clientlib/ApiClient.java +++ b/src/main/java/com/twitter/clientlib/ApiClient.java @@ -29,6 +29,7 @@ import okhttp3.logging.HttpLoggingInterceptor.Level; import okio.Buffer; import okio.BufferedSink; +import okio.BufferedSource; import okio.Okio; import org.apache.oltu.oauth2.client.request.OAuthClientRequest.TokenRequestBuilder; import org.apache.oltu.oauth2.common.message.types.GrantType; @@ -1099,11 +1100,10 @@ public ApiResponse execute(Call call, Type returnType) throws ApiExceptio *

Execute stream.

* * @param call a {@link okhttp3.Call} object - * @param returnType a {@link java.lang.reflect.Type} object * @return a {@link java.io.InputStream} object * @throws com.twitter.clientlib.ApiException if any. */ - public InputStream executeStream(Call call, Type returnType) throws ApiException { + public BufferedSource executeStream(Call call) throws ApiException { try { Response response = call.execute(); if (!response.isSuccessful()) { @@ -1112,7 +1112,7 @@ public InputStream executeStream(Call call, Type returnType) throws ApiException if (response.body() == null) { return null; } - return response.body().byteStream(); + return response.body().source(); } catch (IOException e) { throw new ApiException(e); } diff --git a/src/main/java/com/twitter/clientlib/api/TweetsApi.java b/src/main/java/com/twitter/clientlib/api/TweetsApi.java index c78fa94..d15df00 100644 --- a/src/main/java/com/twitter/clientlib/api/TweetsApi.java +++ b/src/main/java/com/twitter/clientlib/api/TweetsApi.java @@ -23,24 +23,15 @@ package com.twitter.clientlib.api; import com.twitter.clientlib.ApiCallback; -import com.twitter.clientlib.ApiClient; -import com.twitter.clientlib.auth.*; import com.twitter.clientlib.ApiException; import com.twitter.clientlib.ApiResponse; -import com.twitter.clientlib.Configuration; import com.twitter.clientlib.Pair; -import com.twitter.clientlib.ProgressRequestBody; -import com.twitter.clientlib.ProgressResponseBody; -import com.github.scribejava.core.model.OAuth2AccessToken; import com.google.gson.reflect.TypeToken; -import java.io.IOException; - import com.twitter.clientlib.model.AddOrDeleteRulesRequest; import com.twitter.clientlib.model.AddOrDeleteRulesResponse; -import com.twitter.clientlib.model.Error; import com.twitter.clientlib.model.FilteredStreamingTweetResponse; import com.twitter.clientlib.model.Get2ListsIdTweetsResponse; import com.twitter.clientlib.model.Get2SpacesIdBuyersResponse; @@ -56,10 +47,11 @@ import com.twitter.clientlib.model.Get2UsersIdMentionsResponse; import com.twitter.clientlib.model.Get2UsersIdTimelinesReverseChronologicalResponse; import com.twitter.clientlib.model.Get2UsersIdTweetsResponse; + +import java.io.InputStream; import java.time.OffsetDateTime; -import com.twitter.clientlib.model.Problem; + import com.twitter.clientlib.model.RulesLookupResponse; -import java.util.Set; import com.twitter.clientlib.model.StreamingTweetResponse; import com.twitter.clientlib.model.TweetCreateRequest; import com.twitter.clientlib.model.TweetCreateResponse; @@ -72,17 +64,16 @@ import com.twitter.clientlib.model.UsersRetweetsCreateRequest; import com.twitter.clientlib.model.UsersRetweetsCreateResponse; import com.twitter.clientlib.model.UsersRetweetsDeleteResponse; +import com.twitter.clientlib.query.StreamQueryParameters; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Arrays; -import java.io.InputStream; -import javax.ws.rs.core.GenericType; +import java.util.Set; -import org.apache.commons.lang3.StringUtils; +import okio.BufferedSource; public class TweetsApi extends ApiCommon { @@ -308,7 +299,7 @@ private okhttp3.Call createTweetValidateBeforeCall(TweetCreateRequest tweetCreat if (tweetCreateRequest == null) { throw new ApiException("Missing the required parameter 'tweetCreateRequest' when calling createTweet(Async)"); } - + okhttp3.Call localVarCall = createTweetCall(tweetCreateRequest, _callback); return localVarCall; @@ -1676,7 +1667,7 @@ private okhttp3.Call hideReplyByIdValidateBeforeCall(TweetHideRequest tweetHideR if (tweetId == null) { throw new ApiException("Missing the required parameter 'tweetId' when calling hideReplyById(Async)"); } - + okhttp3.Call localVarCall = hideReplyByIdCall(tweetHideRequest, tweetId, _callback); return localVarCall; @@ -2105,7 +2096,7 @@ public okhttp3.Call executeAsync(final ApiCallback _c public APIlistsIdTweetsRequest listsIdTweets(String id) { return new APIlistsIdTweetsRequest(id); } - private okhttp3.Call sampleStreamCall(Integer backfillMinutes, Set tweetFields, Set expansions, Set mediaFields, Set pollFields, Set userFields, Set placeFields, final ApiCallback _callback) throws ApiException { + public okhttp3.Call sampleStreamCall(StreamQueryParameters streamParameters, final ApiCallback _callback) throws ApiException { Object localVarPostBody = null; // create path and map variables @@ -2117,32 +2108,32 @@ private okhttp3.Call sampleStreamCall(Integer backfillMinutes, Set tweet Map localVarCookieParams = new HashMap(); Map localVarFormParams = new HashMap(); - if (backfillMinutes != null) { - localVarQueryParams.addAll(localVarApiClient.parameterToPair("backfill_minutes", backfillMinutes)); + if (streamParameters.getExpansions() != null && !streamParameters.getExpansions().isEmpty()) { + localVarCollectionQueryParams.addAll(localVarApiClient.parameterToPairs("csv", "expansions", streamParameters.getExpansions())); } - if (tweetFields != null) { - localVarCollectionQueryParams.addAll(localVarApiClient.parameterToPairs("csv", "tweet.fields", tweetFields)); + if (streamParameters.getTweetFields() != null && !streamParameters.getTweetFields().isEmpty()) { + localVarCollectionQueryParams.addAll(localVarApiClient.parameterToPairs("csv", "tweet.fields", streamParameters.getTweetFields())); } - if (expansions != null) { - localVarCollectionQueryParams.addAll(localVarApiClient.parameterToPairs("csv", "expansions", expansions)); + if (streamParameters.getUserFields() != null && !streamParameters.getUserFields().isEmpty()) { + localVarCollectionQueryParams.addAll(localVarApiClient.parameterToPairs("csv", "user.fields", streamParameters.getUserFields())); } - if (mediaFields != null) { - localVarCollectionQueryParams.addAll(localVarApiClient.parameterToPairs("csv", "media.fields", mediaFields)); + if (streamParameters.getMediaFields() != null && !streamParameters.getMediaFields().isEmpty()) { + localVarCollectionQueryParams.addAll(localVarApiClient.parameterToPairs("csv", "media.fields", streamParameters.getMediaFields())); } - if (pollFields != null) { - localVarCollectionQueryParams.addAll(localVarApiClient.parameterToPairs("csv", "poll.fields", pollFields)); + if (streamParameters.getPlaceFields() != null && !streamParameters.getPlaceFields().isEmpty()) { + localVarCollectionQueryParams.addAll(localVarApiClient.parameterToPairs("csv", "place.fields", streamParameters.getPlaceFields())); } - if (userFields != null) { - localVarCollectionQueryParams.addAll(localVarApiClient.parameterToPairs("csv", "user.fields", userFields)); + if (streamParameters.getPollFields() != null && !streamParameters.getPollFields().isEmpty()) { + localVarCollectionQueryParams.addAll(localVarApiClient.parameterToPairs("csv", "poll.fields", streamParameters.getPollFields())); } - if (placeFields != null) { - localVarCollectionQueryParams.addAll(localVarApiClient.parameterToPairs("csv", "place.fields", placeFields)); + if (streamParameters.getBackFillMinutes() != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("backfill_minutes", streamParameters.getBackFillMinutes())); } final String[] localVarAccepts = { @@ -2166,113 +2157,47 @@ private okhttp3.Call sampleStreamCall(Integer backfillMinutes, Set tweet } @SuppressWarnings("rawtypes") - private okhttp3.Call sampleStreamValidateBeforeCall(Integer backfillMinutes, Set tweetFields, Set expansions, Set mediaFields, Set pollFields, Set userFields, Set placeFields, final ApiCallback _callback) throws ApiException { + private okhttp3.Call sampleStreamValidateBeforeCall(StreamQueryParameters streamParameters, final ApiCallback _callback) throws ApiException { - okhttp3.Call localVarCall = sampleStreamCall(backfillMinutes, tweetFields, expansions, mediaFields, pollFields, userFields, placeFields, _callback); + okhttp3.Call localVarCall = sampleStreamCall(streamParameters, _callback); return localVarCall; } - - private InputStream sampleStreamWithHttpInfo(Integer backfillMinutes, Set tweetFields, Set expansions, Set mediaFields, Set pollFields, Set userFields, Set placeFields) throws ApiException { - okhttp3.Call localVarCall = sampleStreamValidateBeforeCall(backfillMinutes, tweetFields, expansions, mediaFields, pollFields, userFields, placeFields, null); + private BufferedSource sampleStreamWithHttpInfo(StreamQueryParameters streamParameters) throws ApiException { + okhttp3.Call localVarCall = sampleStreamValidateBeforeCall(streamParameters, null); try { Type localVarReturnType = new TypeToken(){}.getType(); - return localVarApiClient.executeStream(localVarCall, localVarReturnType); + return localVarApiClient.executeStream(localVarCall); } catch (ApiException e) { e.setErrorObject(localVarApiClient.getJSON().getGson().fromJson(e.getResponseBody(), new TypeToken(){}.getType())); throw e; } } - - private okhttp3.Call sampleStreamAsync(Integer backfillMinutes, Set tweetFields, Set expansions, Set mediaFields, Set pollFields, Set userFields, Set placeFields, final ApiCallback _callback) throws ApiException { - okhttp3.Call localVarCall = sampleStreamValidateBeforeCall(backfillMinutes, tweetFields, expansions, mediaFields, pollFields, userFields, placeFields, _callback); + private okhttp3.Call sampleStreamAsync(StreamQueryParameters streamParameters, final ApiCallback _callback) throws ApiException { + + okhttp3.Call localVarCall = sampleStreamValidateBeforeCall(streamParameters, _callback); Type localVarReturnType = new TypeToken(){}.getType(); localVarApiClient.executeAsync(localVarCall, localVarReturnType, _callback); return localVarCall; } public class APIsampleStreamRequest { - private Integer backfillMinutes; - private Set tweetFields; - private Set expansions; - private Set mediaFields; - private Set pollFields; - private Set userFields; - private Set placeFields; + private StreamQueryParameters parameters; private APIsampleStreamRequest() { + parameters = new StreamQueryParameters.Builder().build(); } /** - * Set backfillMinutes - * @param backfillMinutes The number of minutes of backfill requested. (optional) - * @return APIsampleStreamRequest - */ - public APIsampleStreamRequest backfillMinutes(Integer backfillMinutes) { - this.backfillMinutes = backfillMinutes; - return this; - } - - /** - * Set tweetFields - * @param tweetFields A comma separated list of Tweet fields to display. (optional) + * Set parameters + * @param queryParameters {@link StreamQueryParameters} The parameters of the request. (optional) * @return APIsampleStreamRequest */ - public APIsampleStreamRequest tweetFields(Set tweetFields) { - this.tweetFields = tweetFields; - return this; - } - - /** - * Set expansions - * @param expansions A comma separated list of fields to expand. (optional) - * @return APIsampleStreamRequest - */ - public APIsampleStreamRequest expansions(Set expansions) { - this.expansions = expansions; - return this; - } - - /** - * Set mediaFields - * @param mediaFields A comma separated list of Media fields to display. (optional) - * @return APIsampleStreamRequest - */ - public APIsampleStreamRequest mediaFields(Set mediaFields) { - this.mediaFields = mediaFields; - return this; - } - - /** - * Set pollFields - * @param pollFields A comma separated list of Poll fields to display. (optional) - * @return APIsampleStreamRequest - */ - public APIsampleStreamRequest pollFields(Set pollFields) { - this.pollFields = pollFields; - return this; - } - - /** - * Set userFields - * @param userFields A comma separated list of User fields to display. (optional) - * @return APIsampleStreamRequest - */ - public APIsampleStreamRequest userFields(Set userFields) { - this.userFields = userFields; - return this; - } - - /** - * Set placeFields - * @param placeFields A comma separated list of Place fields to display. (optional) - * @return APIsampleStreamRequest - */ - public APIsampleStreamRequest placeFields(Set placeFields) { - this.placeFields = placeFields; + public APIsampleStreamRequest parameters(StreamQueryParameters queryParameters) { + this.parameters = queryParameters; return this; } @@ -2289,7 +2214,7 @@ public APIsampleStreamRequest placeFields(Set placeFields) { */ public okhttp3.Call buildCall(final ApiCallback _callback) throws ApiException { - return sampleStreamCall(backfillMinutes, tweetFields, expansions, mediaFields, pollFields, userFields, placeFields, _callback); + return sampleStreamCall(parameters, _callback); } /** @@ -2303,27 +2228,27 @@ public okhttp3.Call buildCall(final ApiCallback _callback) throws ApiException { 0 The request has failed. - */ - public InputStream execute() throws ApiException { - return sampleStreamWithHttpInfo(backfillMinutes, tweetFields, expansions, mediaFields, pollFields, userFields, placeFields); + public BufferedSource execute() throws ApiException { + return sampleStreamWithHttpInfo(parameters); } /** - * Calls the API using a retry mechanism to handle rate limits errors. - * - */ - public InputStream execute(Integer retries) throws ApiException { - InputStream localVarResp; - try{ - localVarResp = execute(); - } - catch (ApiException e) { - if(handleRateLimit(e, retries)) { - return execute(retries - 1); - } else { - throw e; + * Calls the API using a retry mechanism to handle rate limits errors. + * + */ + public BufferedSource execute(Integer retries) throws ApiException { + BufferedSource localVarResp; + try{ + localVarResp = execute(); } - } - return localVarResp; + catch (ApiException e) { + if(handleRateLimit(e, retries)) { + return execute(retries - 1); + } else { + throw e; + } + } + return localVarResp; } /** * Execute sampleStream request with HTTP info returned @@ -2337,9 +2262,9 @@ public InputStream execute(Integer retries) throws ApiException { */ - public InputStream executeWithHttpInfo() throws ApiException { - return sampleStreamWithHttpInfo(backfillMinutes, tweetFields, expansions, mediaFields, pollFields, userFields, placeFields); - } + public BufferedSource executeWithHttpInfo() throws ApiException { + return sampleStreamWithHttpInfo(parameters); + } /** * Execute sampleStream request (asynchronously) * @param _callback The callback to be executed when the API call finishes @@ -2353,7 +2278,7 @@ public InputStream executeWithHttpInfo() throws ApiException { */ public okhttp3.Call executeAsync(final ApiCallback _callback) throws ApiException { - return sampleStreamAsync(backfillMinutes, tweetFields, expansions, mediaFields, pollFields, userFields, placeFields, _callback); + return sampleStreamAsync(parameters, _callback); } } @@ -2371,7 +2296,7 @@ public okhttp3.Call executeAsync(final ApiCallback _call public APIsampleStreamRequest sampleStream() { return new APIsampleStreamRequest(); } - private okhttp3.Call searchStreamCall(Integer backfillMinutes, Set tweetFields, Set expansions, Set mediaFields, Set pollFields, Set userFields, Set placeFields, final ApiCallback _callback) throws ApiException { + public okhttp3.Call searchStreamCall(StreamQueryParameters streamParameters, final ApiCallback _callback) throws ApiException { Object localVarPostBody = null; // create path and map variables @@ -2383,32 +2308,32 @@ private okhttp3.Call searchStreamCall(Integer backfillMinutes, Set tweet Map localVarCookieParams = new HashMap(); Map localVarFormParams = new HashMap(); - if (backfillMinutes != null) { - localVarQueryParams.addAll(localVarApiClient.parameterToPair("backfill_minutes", backfillMinutes)); + if (streamParameters.getExpansions() != null && !streamParameters.getExpansions().isEmpty()) { + localVarCollectionQueryParams.addAll(localVarApiClient.parameterToPairs("csv", "expansions", streamParameters.getExpansions())); } - if (tweetFields != null) { - localVarCollectionQueryParams.addAll(localVarApiClient.parameterToPairs("csv", "tweet.fields", tweetFields)); + if (streamParameters.getTweetFields() != null && !streamParameters.getTweetFields().isEmpty()) { + localVarCollectionQueryParams.addAll(localVarApiClient.parameterToPairs("csv", "tweet.fields", streamParameters.getTweetFields())); } - if (expansions != null) { - localVarCollectionQueryParams.addAll(localVarApiClient.parameterToPairs("csv", "expansions", expansions)); + if (streamParameters.getUserFields() != null && !streamParameters.getUserFields().isEmpty()) { + localVarCollectionQueryParams.addAll(localVarApiClient.parameterToPairs("csv", "user.fields", streamParameters.getUserFields())); } - if (mediaFields != null) { - localVarCollectionQueryParams.addAll(localVarApiClient.parameterToPairs("csv", "media.fields", mediaFields)); + if (streamParameters.getMediaFields() != null && !streamParameters.getMediaFields().isEmpty()) { + localVarCollectionQueryParams.addAll(localVarApiClient.parameterToPairs("csv", "media.fields", streamParameters.getMediaFields())); } - if (pollFields != null) { - localVarCollectionQueryParams.addAll(localVarApiClient.parameterToPairs("csv", "poll.fields", pollFields)); + if (streamParameters.getPlaceFields() != null && !streamParameters.getPlaceFields().isEmpty()) { + localVarCollectionQueryParams.addAll(localVarApiClient.parameterToPairs("csv", "place.fields", streamParameters.getPlaceFields())); } - if (userFields != null) { - localVarCollectionQueryParams.addAll(localVarApiClient.parameterToPairs("csv", "user.fields", userFields)); + if (streamParameters.getPollFields() != null && !streamParameters.getPollFields().isEmpty()) { + localVarCollectionQueryParams.addAll(localVarApiClient.parameterToPairs("csv", "poll.fields", streamParameters.getPollFields())); } - if (placeFields != null) { - localVarCollectionQueryParams.addAll(localVarApiClient.parameterToPairs("csv", "place.fields", placeFields)); + if (streamParameters.getBackFillMinutes() != null) { + localVarQueryParams.addAll(localVarApiClient.parameterToPair("backfill_minutes", streamParameters.getBackFillMinutes())); } final String[] localVarAccepts = { @@ -2432,113 +2357,47 @@ private okhttp3.Call searchStreamCall(Integer backfillMinutes, Set tweet } @SuppressWarnings("rawtypes") - private okhttp3.Call searchStreamValidateBeforeCall(Integer backfillMinutes, Set tweetFields, Set expansions, Set mediaFields, Set pollFields, Set userFields, Set placeFields, final ApiCallback _callback) throws ApiException { + private okhttp3.Call searchStreamValidateBeforeCall(StreamQueryParameters streamParameters, final ApiCallback _callback) throws ApiException { - okhttp3.Call localVarCall = searchStreamCall(backfillMinutes, tweetFields, expansions, mediaFields, pollFields, userFields, placeFields, _callback); + okhttp3.Call localVarCall = searchStreamCall(streamParameters, _callback); return localVarCall; } - - private InputStream searchStreamWithHttpInfo(Integer backfillMinutes, Set tweetFields, Set expansions, Set mediaFields, Set pollFields, Set userFields, Set placeFields) throws ApiException { - okhttp3.Call localVarCall = searchStreamValidateBeforeCall(backfillMinutes, tweetFields, expansions, mediaFields, pollFields, userFields, placeFields, null); + private BufferedSource searchStreamWithHttpInfo(StreamQueryParameters queryParameters) throws ApiException { + okhttp3.Call localVarCall = searchStreamValidateBeforeCall(queryParameters, null); try { Type localVarReturnType = new TypeToken(){}.getType(); - return localVarApiClient.executeStream(localVarCall, localVarReturnType); + return localVarApiClient.executeStream(localVarCall); } catch (ApiException e) { e.setErrorObject(localVarApiClient.getJSON().getGson().fromJson(e.getResponseBody(), new TypeToken(){}.getType())); throw e; } } - - private okhttp3.Call searchStreamAsync(Integer backfillMinutes, Set tweetFields, Set expansions, Set mediaFields, Set pollFields, Set userFields, Set placeFields, final ApiCallback _callback) throws ApiException { - okhttp3.Call localVarCall = searchStreamValidateBeforeCall(backfillMinutes, tweetFields, expansions, mediaFields, pollFields, userFields, placeFields, _callback); + private okhttp3.Call searchStreamAsync(StreamQueryParameters streamParameters, final ApiCallback _callback) throws ApiException { + + okhttp3.Call localVarCall = searchStreamValidateBeforeCall(streamParameters, _callback); Type localVarReturnType = new TypeToken(){}.getType(); localVarApiClient.executeAsync(localVarCall, localVarReturnType, _callback); return localVarCall; } public class APIsearchStreamRequest { - private Integer backfillMinutes; - private Set tweetFields; - private Set expansions; - private Set mediaFields; - private Set pollFields; - private Set userFields; - private Set placeFields; + private StreamQueryParameters parameters; private APIsearchStreamRequest() { + parameters = new StreamQueryParameters.Builder().build(); } /** - * Set backfillMinutes - * @param backfillMinutes The number of minutes of backfill requested. (optional) - * @return APIsearchStreamRequest - */ - public APIsearchStreamRequest backfillMinutes(Integer backfillMinutes) { - this.backfillMinutes = backfillMinutes; - return this; - } - - /** - * Set tweetFields - * @param tweetFields A comma separated list of Tweet fields to display. (optional) - * @return APIsearchStreamRequest - */ - public APIsearchStreamRequest tweetFields(Set tweetFields) { - this.tweetFields = tweetFields; - return this; - } - - /** - * Set expansions - * @param expansions A comma separated list of fields to expand. (optional) + * Set parameters + * @param queryParameters {@link StreamQueryParameters} The parameters of the request. (optional) * @return APIsearchStreamRequest */ - public APIsearchStreamRequest expansions(Set expansions) { - this.expansions = expansions; - return this; - } - - /** - * Set mediaFields - * @param mediaFields A comma separated list of Media fields to display. (optional) - * @return APIsearchStreamRequest - */ - public APIsearchStreamRequest mediaFields(Set mediaFields) { - this.mediaFields = mediaFields; - return this; - } - - /** - * Set pollFields - * @param pollFields A comma separated list of Poll fields to display. (optional) - * @return APIsearchStreamRequest - */ - public APIsearchStreamRequest pollFields(Set pollFields) { - this.pollFields = pollFields; - return this; - } - - /** - * Set userFields - * @param userFields A comma separated list of User fields to display. (optional) - * @return APIsearchStreamRequest - */ - public APIsearchStreamRequest userFields(Set userFields) { - this.userFields = userFields; - return this; - } - - /** - * Set placeFields - * @param placeFields A comma separated list of Place fields to display. (optional) - * @return APIsearchStreamRequest - */ - public APIsearchStreamRequest placeFields(Set placeFields) { - this.placeFields = placeFields; + public APIsearchStreamRequest parameters(StreamQueryParameters queryParameters) { + this.parameters = queryParameters; return this; } @@ -2555,7 +2414,7 @@ public APIsearchStreamRequest placeFields(Set placeFields) { */ public okhttp3.Call buildCall(final ApiCallback _callback) throws ApiException { - return searchStreamCall(backfillMinutes, tweetFields, expansions, mediaFields, pollFields, userFields, placeFields, _callback); + return searchStreamCall(parameters, _callback); } /** @@ -2569,27 +2428,27 @@ public okhttp3.Call buildCall(final ApiCallback _callback) throws ApiException { 0 The request has failed. - */ - public InputStream execute() throws ApiException { - return searchStreamWithHttpInfo(backfillMinutes, tweetFields, expansions, mediaFields, pollFields, userFields, placeFields); + public BufferedSource execute() throws ApiException { + return searchStreamWithHttpInfo(parameters); } /** - * Calls the API using a retry mechanism to handle rate limits errors. - * - */ - public InputStream execute(Integer retries) throws ApiException { - InputStream localVarResp; - try{ - localVarResp = execute(); - } - catch (ApiException e) { - if(handleRateLimit(e, retries)) { - return execute(retries - 1); - } else { - throw e; + * Calls the API using a retry mechanism to handle rate limits errors. + * + */ + public BufferedSource execute(Integer retries) throws ApiException { + BufferedSource localVarResp; + try{ + localVarResp = execute(); } - } - return localVarResp; + catch (ApiException e) { + if(handleRateLimit(e, retries)) { + return execute(retries - 1); + } else { + throw e; + } + } + return localVarResp; } /** * Execute searchStream request with HTTP info returned @@ -2602,10 +2461,9 @@ public InputStream execute(Integer retries) throws ApiException { 0 The request has failed. - */ - - public InputStream executeWithHttpInfo() throws ApiException { - return searchStreamWithHttpInfo(backfillMinutes, tweetFields, expansions, mediaFields, pollFields, userFields, placeFields); - } + public BufferedSource executeWithHttpInfo() throws ApiException { + return searchStreamWithHttpInfo(parameters); + } /** * Execute searchStream request (asynchronously) * @param _callback The callback to be executed when the API call finishes @@ -2619,7 +2477,7 @@ public InputStream executeWithHttpInfo() throws ApiException { */ public okhttp3.Call executeAsync(final ApiCallback _callback) throws ApiException { - return searchStreamAsync(backfillMinutes, tweetFields, expansions, mediaFields, pollFields, userFields, placeFields, _callback); + return searchStreamAsync(parameters, _callback); } } diff --git a/src/main/java/com/twitter/clientlib/exceptions/AuthenticationException.java b/src/main/java/com/twitter/clientlib/exceptions/AuthenticationException.java new file mode 100644 index 0000000..11f0313 --- /dev/null +++ b/src/main/java/com/twitter/clientlib/exceptions/AuthenticationException.java @@ -0,0 +1,29 @@ +/* +Copyright 2020 Twitter, Inc. +SPDX-License-Identifier: Apache-2.0 + +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 com.twitter.clientlib.exceptions; + +public class AuthenticationException extends RuntimeException { + + public AuthenticationException(String message) { + super(message); + } + + public AuthenticationException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/com/twitter/clientlib/exceptions/EmptyStreamTimeoutException.java b/src/main/java/com/twitter/clientlib/exceptions/EmptyStreamTimeoutException.java new file mode 100644 index 0000000..bf422c3 --- /dev/null +++ b/src/main/java/com/twitter/clientlib/exceptions/EmptyStreamTimeoutException.java @@ -0,0 +1,25 @@ +/* +Copyright 2020 Twitter, Inc. +SPDX-License-Identifier: Apache-2.0 + +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 com.twitter.clientlib.exceptions; + +public class EmptyStreamTimeoutException extends RuntimeException { + public EmptyStreamTimeoutException(String message) { + super(message); + } +} diff --git a/src/main/java/com/twitter/clientlib/exceptions/StreamException.java b/src/main/java/com/twitter/clientlib/exceptions/StreamException.java new file mode 100644 index 0000000..f921dd8 --- /dev/null +++ b/src/main/java/com/twitter/clientlib/exceptions/StreamException.java @@ -0,0 +1,30 @@ +/* +Copyright 2020 Twitter, Inc. +SPDX-License-Identifier: Apache-2.0 + +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 com.twitter.clientlib.exceptions; + +public class StreamException extends RuntimeException{ + + public StreamException(String message) { + super(message); + } + + public StreamException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/com/twitter/clientlib/exceptions/TooManyRequestsException.java b/src/main/java/com/twitter/clientlib/exceptions/TooManyRequestsException.java new file mode 100644 index 0000000..705d4b0 --- /dev/null +++ b/src/main/java/com/twitter/clientlib/exceptions/TooManyRequestsException.java @@ -0,0 +1,30 @@ +/* +Copyright 2020 Twitter, Inc. +SPDX-License-Identifier: Apache-2.0 + +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 com.twitter.clientlib.exceptions; + +public class TooManyRequestsException extends RuntimeException{ + + public TooManyRequestsException(String message) { + super(message); + } + + public TooManyRequestsException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/main/java/com/twitter/clientlib/model/UrlEntity.java b/src/main/java/com/twitter/clientlib/model/UrlEntity.java index 099e0fd..df9d8d4 100644 --- a/src/main/java/com/twitter/clientlib/model/UrlEntity.java +++ b/src/main/java/com/twitter/clientlib/model/UrlEntity.java @@ -23,15 +23,11 @@ package com.twitter.clientlib.model; import java.util.Objects; -import java.util.Arrays; + import com.google.gson.TypeAdapter; -import com.google.gson.annotations.JsonAdapter; import com.google.gson.annotations.SerializedName; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; -import com.twitter.clientlib.model.EntityIndicesInclusiveExclusive; -import com.twitter.clientlib.model.UrlFields; -import com.twitter.clientlib.model.UrlImage; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import java.io.IOException; @@ -40,22 +36,13 @@ import java.util.List; import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import com.google.gson.JsonArray; -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; import com.google.gson.TypeAdapterFactory; import com.google.gson.reflect.TypeToken; -import java.lang.reflect.Type; -import java.util.HashMap; import java.util.HashSet; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; import com.twitter.clientlib.JSON; diff --git a/src/main/java/com/twitter/clientlib/model/URLFields.java b/src/main/java/com/twitter/clientlib/model/UrlFields.java similarity index 97% rename from src/main/java/com/twitter/clientlib/model/URLFields.java rename to src/main/java/com/twitter/clientlib/model/UrlFields.java index 36ad728..d9040b3 100644 --- a/src/main/java/com/twitter/clientlib/model/URLFields.java +++ b/src/main/java/com/twitter/clientlib/model/UrlFields.java @@ -23,13 +23,11 @@ package com.twitter.clientlib.model; import java.util.Objects; -import java.util.Arrays; + import com.google.gson.TypeAdapter; -import com.google.gson.annotations.JsonAdapter; import com.google.gson.annotations.SerializedName; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; -import com.twitter.clientlib.model.UrlImage; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import java.io.IOException; @@ -38,22 +36,13 @@ import java.util.List; import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import com.google.gson.JsonArray; -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; import com.google.gson.TypeAdapterFactory; import com.google.gson.reflect.TypeToken; -import java.lang.reflect.Type; -import java.util.HashMap; import java.util.HashSet; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; import com.twitter.clientlib.JSON; diff --git a/src/main/java/com/twitter/clientlib/model/URLImage.java b/src/main/java/com/twitter/clientlib/model/UrlImage.java similarity index 94% rename from src/main/java/com/twitter/clientlib/model/URLImage.java rename to src/main/java/com/twitter/clientlib/model/UrlImage.java index c766943..9c0913d 100644 --- a/src/main/java/com/twitter/clientlib/model/URLImage.java +++ b/src/main/java/com/twitter/clientlib/model/UrlImage.java @@ -23,9 +23,8 @@ package com.twitter.clientlib.model; import java.util.Objects; -import java.util.Arrays; + import com.google.gson.TypeAdapter; -import com.google.gson.annotations.JsonAdapter; import com.google.gson.annotations.SerializedName; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; @@ -35,22 +34,12 @@ import java.net.URL; import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonArray; -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; import com.google.gson.TypeAdapterFactory; import com.google.gson.reflect.TypeToken; -import java.lang.reflect.Type; -import java.util.HashMap; import java.util.HashSet; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; import com.twitter.clientlib.JSON; @@ -72,7 +61,7 @@ public class UrlImage { @SerializedName(SERIALIZED_NAME_WIDTH) private Integer width; - public UrlImage() { + public UrlImage() { } public UrlImage height(Integer height) { diff --git a/src/main/java/com/twitter/clientlib/query/StreamQueryParameters.java b/src/main/java/com/twitter/clientlib/query/StreamQueryParameters.java new file mode 100644 index 0000000..9f74a59 --- /dev/null +++ b/src/main/java/com/twitter/clientlib/query/StreamQueryParameters.java @@ -0,0 +1,257 @@ +/* +Copyright 2020 Twitter, Inc. +SPDX-License-Identifier: Apache-2.0 + +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 com.twitter.clientlib.query; + +import com.twitter.clientlib.query.model.*; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +public class StreamQueryParameters { + private List tweetFields; + private List expansions; + private List mediaFields; + private List pollFields; + private List userFields; + private List placeFields; + private Integer backFillMinutes; + + private StreamQueryParameters(Builder builder) { + this.tweetFields = builder.tweetFields; + this.expansions = builder.expansions; + this.mediaFields = builder.mediaFields; + this.pollFields = builder.pollFields; + this.userFields = builder.userFields; + this.placeFields = builder.placeFields; + this.backFillMinutes = builder.backFillMinutes; + } + + public List getTweetFields() { + return tweetFields.stream().map(TweetField::getName).collect(Collectors.toList()); + } + + public void setTweetFields(List tweetFields) { + this.tweetFields = tweetFields; + } + + public List getExpansions() { + return expansions.stream().map(Expansion::getName).collect(Collectors.toList()); + } + + public void setExpansions(List expansions) { + this.expansions = expansions; + } + + public List getMediaFields() { + return mediaFields.stream().map(MediaField::getName).collect(Collectors.toList()); + } + + public void setMediaFields(List mediaFields) { + this.mediaFields = mediaFields; + } + + public List getPollFields() { + return pollFields.stream().map(PollField::getName).collect(Collectors.toList()); + } + + public void setPollFields(List pollFields) { + this.pollFields = pollFields; + } + + public List getUserFields() { + return userFields.stream().map(UserField::getName).collect(Collectors.toList()); + } + + public void setUserFields(List userFields) { + this.userFields = userFields; + } + + public List getPlaceFields() { + return placeFields.stream().map(PlaceField::getName).collect(Collectors.toList()); + } + + public void setPlaceFields(List placeFields) { + this.placeFields = placeFields; + } + + public Integer getBackFillMinutes() { + return backFillMinutes; + } + + public void setBackFillMinutes(Integer backFillMinutes) { + this.backFillMinutes = backFillMinutes; + } + + public static class Builder { + private final List tweetFields; + private final List expansions; + private final List mediaFields; + private final List pollFields; + private final List userFields; + private final List placeFields; + private Integer backFillMinutes = 0; + + public Builder() { + this.tweetFields = new ArrayList<>(); + this.expansions = new ArrayList<>(); + this.mediaFields = new ArrayList<>(); + this.pollFields = new ArrayList<>(); + this.userFields = new ArrayList<>(); + this.placeFields = new ArrayList<>(); + } + + /** + * Set tweetFields + * @param tweetFields A comma separated list of Tweet fields to display. (optional) + * @return Builder + */ + public Builder withTweetFields(TweetField... tweetFields) { + this.tweetFields.addAll(Arrays.asList(tweetFields)); + return this; + } + + /** + * Set tweetFields + * @param tweetFields A comma separated list of Tweet fields to display. (optional) + * @return Builder + */ + public Builder withTweetFields(Collection tweetFields) { + this.tweetFields.addAll(tweetFields); + return this; + } + + /** + * Set mediaFields + * @param mediaFields A comma separated list of Media fields to display. (optional) + * @return Builder + */ + public Builder withMediaFields(MediaField... mediaFields) { + this.mediaFields.addAll(Arrays.asList(mediaFields)); + return this; + } + + /** + * Set mediaFields + * @param mediaFields A comma separated list of Media fields to display. (optional) + * @return Builder + */ + public Builder withMediaFields(Collection mediaFields) { + this.mediaFields.addAll(mediaFields); + return this; + } + + /** + * Set pollFields + * @param pollFields A comma separated list of Poll fields to display. (optional) + * @return Builder + */ + public Builder withPollFields(PollField... pollFields) { + this.pollFields.addAll(Arrays.asList(pollFields)); + return this; + } + + /** + * Set pollFields + * @param pollFields A comma separated list of Poll fields to display. (optional) + * @return Builder + */ + public Builder withPollFields(Collection pollFields) { + this.pollFields.addAll(pollFields); + return this; + } + + /** + * Set userFields + * @param userFields A comma separated list of User fields to display. (optional) + * @return Builder + */ + public Builder withUserFields(UserField... userFields) { + this.userFields.addAll(Arrays.asList(userFields)); + return this; + } + + /** + * Set userFields + * @param userFields A comma separated list of User fields to display. (optional) + * @return Builder + */ + public Builder withUserFields(Collection userFields) { + this.userFields.addAll(userFields); + return this; + } + + /** + * Set placeFields + * @param placeFields A comma separated list of Place fields to display. (optional) + * @return Builder + */ + public Builder withPlaceFields(PlaceField... placeFields) { + this.placeFields.addAll(Arrays.asList(placeFields)); + return this; + } + + /** + * Set placeFields + * @param placeFields A comma separated list of Place fields to display. (optional) + * @return Builder + */ + public Builder withPlaceFields(Collection placeFields) { + this.placeFields.addAll(placeFields); + return this; + } + + /** + * Set expansions + * @param expansions A comma separated list of fields to expand. (optional) + * @return Builder + */ + public Builder withExpansions(Expansion... expansions) { + this.expansions.addAll(Arrays.asList(expansions)); + return this; + } + + /** + * Set expansions + * @param expansions A comma separated list of fields to expand. (optional) + * @return Builder + */ + public Builder withExpansions(Collection expansions) { + this.expansions.addAll(expansions); + return this; + } + + /** + * Set backfillMinutes + * @param backfillMinutes The number of minutes of backfill requested. (optional) + * @return Builder + */ + public Builder withBackfillMinutes(Integer backFillMinutes) { + this.backFillMinutes = backFillMinutes; + return this; + } + + public StreamQueryParameters build() { + return new StreamQueryParameters(this); + } + } +} diff --git a/src/main/java/com/twitter/clientlib/query/model/Expansion.java b/src/main/java/com/twitter/clientlib/query/model/Expansion.java new file mode 100644 index 0000000..e7c0fa8 --- /dev/null +++ b/src/main/java/com/twitter/clientlib/query/model/Expansion.java @@ -0,0 +1,48 @@ +/* +Copyright 2020 Twitter, Inc. +SPDX-License-Identifier: Apache-2.0 + +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 com.twitter.clientlib.query.model; + +import java.util.Arrays; +import java.util.List; + +public enum Expansion { + ATTACHMENTS_MEDIA_KEYS("attachments.media_keys"), + ATTACHMENTS_POLL_IDS("attachments.poll_ids"), + AUTHOR_ID("author_id"), + ENTITIES_MENTIONS_USERNAME("entities.mentions.username"), + GEO_PLACE_ID("geo.place_id"), + IN_REPLY_TO_USER_ID("in_reply_to_user_id"), + REFERENCED_TWEETS_ID("referenced_tweets.id"), + REFERENCED_TWEETS_ID_AUTHOR_ID("referenced_tweets.id.author_id"); + + private final String name; + + Expansion(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public static List all() { + return Arrays.asList(Expansion.values()); + } + +} diff --git a/src/main/java/com/twitter/clientlib/query/model/MediaField.java b/src/main/java/com/twitter/clientlib/query/model/MediaField.java new file mode 100644 index 0000000..d27d7f0 --- /dev/null +++ b/src/main/java/com/twitter/clientlib/query/model/MediaField.java @@ -0,0 +1,50 @@ +/* +Copyright 2020 Twitter, Inc. +SPDX-License-Identifier: Apache-2.0 + +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 com.twitter.clientlib.query.model; + +import java.util.Arrays; +import java.util.List; + +public enum MediaField { + ALT_TEXT("alt_text"), + DURATION_MS("duration_ms"), + HEIGHT("height"), + MEDIA_KEY("media_key"), + PREVIEW_IMAGE_URL("preview_image_url"), + PUBLIC_METRICS("public_metrics"), + TYPE("type"), + URL("url"), + VARIANTS("variants"), + WIDTH("width"),; + + private final String name; + + MediaField(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public static List all() { + return Arrays.asList(MediaField.values()); + } + +} diff --git a/src/main/java/com/twitter/clientlib/query/model/PlaceField.java b/src/main/java/com/twitter/clientlib/query/model/PlaceField.java new file mode 100644 index 0000000..08ddd3a --- /dev/null +++ b/src/main/java/com/twitter/clientlib/query/model/PlaceField.java @@ -0,0 +1,47 @@ +/* +Copyright 2020 Twitter, Inc. +SPDX-License-Identifier: Apache-2.0 + +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 com.twitter.clientlib.query.model; + +import java.util.Arrays; +import java.util.List; + +public enum PlaceField { + CONTAINED_WITHIN("contained_within"), + COUNTRY("country"), + COUNTRY_CODE("country_code"), + FULL_NAME("full_name"), + GEO("geo"), + ID("id"), + NAME("name"), + PLACE_TYPE("place_type"); + + private final String name; + + PlaceField(String name) { + this.name = name; + } + + public static List all() { + return Arrays.asList(PlaceField.values()); + } + + public String getName() { + return name; + } +} diff --git a/src/main/java/com/twitter/clientlib/query/model/PollField.java b/src/main/java/com/twitter/clientlib/query/model/PollField.java new file mode 100644 index 0000000..d779f6e --- /dev/null +++ b/src/main/java/com/twitter/clientlib/query/model/PollField.java @@ -0,0 +1,43 @@ +/* +Copyright 2020 Twitter, Inc. +SPDX-License-Identifier: Apache-2.0 + +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 com.twitter.clientlib.query.model; + +import java.util.Arrays; +import java.util.List; + +public enum PollField { + DURATION_MINUTES("duration_minutes"), + END_DATETIME("end_datetime"), + ID("id"), + OPTIONS("options"), + VOTING_STATUS("voting_status"); + + private final String name; + + PollField(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public static List all() { + return Arrays.asList(PollField.values()); + } +} diff --git a/src/main/java/com/twitter/clientlib/query/model/TweetField.java b/src/main/java/com/twitter/clientlib/query/model/TweetField.java new file mode 100644 index 0000000..7644022 --- /dev/null +++ b/src/main/java/com/twitter/clientlib/query/model/TweetField.java @@ -0,0 +1,56 @@ +/* +Copyright 2020 Twitter, Inc. +SPDX-License-Identifier: Apache-2.0 + +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 com.twitter.clientlib.query.model; + +import java.util.Arrays; +import java.util.List; + +public enum TweetField { + ATTACHMENTS("attachments"), + AUTHOR_ID("author_id"), + CONTEXT_ANNOTATIONS("context_annotations"), + CONVERSATION_ID("conversation_id"), + CREATED_AT("created_at"), + ENTITIES("entities"), + GEO("geo"), + ID("id"), + IN_REPLY_TO_USER_ID("in_reply_to_user_id"), + LANG("lang"), + POSSIBLE_SENSITIVE("possibly_sensitive"), + PUBLIC_METRICS("public_metrics"), + REFERENCED_TWEETS("referenced_tweets"), + REPLY_SETTINGS("reply_settings"), + SOURCE("source"), + TEXT("text"), + WITHHELD("withheld"); + + private final String name; + + TweetField(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + public static List all() { + return Arrays.asList(TweetField.values()); + } + +} diff --git a/src/main/java/com/twitter/clientlib/query/model/UserField.java b/src/main/java/com/twitter/clientlib/query/model/UserField.java new file mode 100644 index 0000000..639d026 --- /dev/null +++ b/src/main/java/com/twitter/clientlib/query/model/UserField.java @@ -0,0 +1,52 @@ +/* +Copyright 2020 Twitter, Inc. +SPDX-License-Identifier: Apache-2.0 + +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 com.twitter.clientlib.query.model; + +import java.util.Arrays; +import java.util.List; + +public enum UserField { + CREATED_AT("created_at"), + DESCRIPTION("description"), + ENTITIES("entities"), + ID("id"), + LOCATION("location"), + NAME("name"), + PINNED_TWEET_ID("pinned_tweet_id"), + PROFILE_IMAGE_URL("profile_image_url"), + PROTECTED("protected"), + PUBLIC_METRICS("public_metrics"), + URL("url"), + USERNAME("username"), + VERIFIED("verified"), + WITHHELD("withheld"); + + private final String name; + + UserField(String name) { + this.name = name; + } + + public static List all() { + return Arrays.asList(UserField.values()); + } + + public String getName() { + return name; + } +} diff --git a/src/main/java/com/twitter/clientlib/stream/TweetsStreamExecutor.java b/src/main/java/com/twitter/clientlib/stream/TweetsStreamExecutor.java new file mode 100644 index 0000000..6ee2d1a --- /dev/null +++ b/src/main/java/com/twitter/clientlib/stream/TweetsStreamExecutor.java @@ -0,0 +1,212 @@ +/* +Copyright 2020 Twitter, Inc. +SPDX-License-Identifier: Apache-2.0 + +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 com.twitter.clientlib.stream; + + +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingDeque; +import java.util.concurrent.TimeUnit; + +import com.fasterxml.jackson.core.JsonProcessingException; + +import com.twitter.clientlib.exceptions.EmptyStreamTimeoutException; +import com.twitter.clientlib.model.StreamingTweetResponse; +import okio.BufferedSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class TweetsStreamExecutor { + + private static final Logger logger = LoggerFactory.getLogger(TweetsStreamExecutor.class); + + private static final long EMPTY_STREAM_TIMEOUT = 20; + private static final int POLL_WAIT = 5; + + private volatile BlockingQueue rawTweets; + private volatile BlockingQueue tweets; + private volatile boolean isRunning = true; + private ExecutorService rawTweetsQueuerService; + private ExecutorService deserializationService; + private ExecutorService listenersService; + private final List listeners = new ArrayList<>(); + private BufferedSource stream; + + public TweetsStreamExecutor(BufferedSource stream) { + this.rawTweets = new LinkedBlockingDeque<>(); + this.tweets = new LinkedBlockingDeque<>(); + this.stream = stream; + } + + public void addListener(TweetsStreamListener toAdd) { + listeners.add(toAdd); + } + + public void removeListener(TweetsStreamListener toRemove) { + listeners.remove(toRemove); + } + + public void start() { + if (stream == null) { + logger.error("Stream is null. Exiting..."); + return; + } + rawTweetsQueuerService = Executors.newSingleThreadExecutor(); + rawTweetsQueuerService.submit(new RawTweetsQueuer()); + + deserializationService = Executors.newSingleThreadExecutor(); + deserializationService.submit(new DeserializeTweetsTask()); + + listenersService = Executors.newSingleThreadExecutor(); + listenersService.submit(new TweetsListenersTask()); + } + + public synchronized void shutdown() { + logger.info("TweetsStreamListenersExecutor is shutting down."); + isRunning = false; + shutDownServices(); + try { + terminateServices(); + stream.close(); + } catch (InterruptedException ie) { + shutDownServices(); + Thread.currentThread().interrupt(); + } catch (IOException e) { + + } + } + + private void shutDownServices() { + rawTweetsQueuerService.shutdown(); + deserializationService.shutdown(); + listenersService.shutdown(); + } + + private void terminateServices() throws InterruptedException { + terminateService(rawTweetsQueuerService); + terminateService(deserializationService); + terminateService(listenersService); + } + private void terminateService(ExecutorService executorService) throws InterruptedException { + if (!executorService.awaitTermination(1500, TimeUnit.MILLISECONDS)) { + executorService.shutdownNow(); + if (!executorService.awaitTermination(1500, TimeUnit.MILLISECONDS)) + logger.error("Thread pool did not terminate"); + } + } + + private class RawTweetsQueuer implements Runnable { + + private final Logger logger = LoggerFactory.getLogger(RawTweetsQueuer.class); + + @Override + public void run() { + queueTweets(); + } + + public void queueTweets() { + String line = null; + try { + boolean emptyResponse = false; + LocalDateTime firstEmpty = LocalDateTime.now(); + while (isRunning) { + line = stream.readUtf8Line(); + if(line == null || line.isEmpty()) { + if(!emptyResponse) { + firstEmpty = LocalDateTime.now(); + emptyResponse = true; + } else { + if(LocalDateTime.now().minus(EMPTY_STREAM_TIMEOUT, ChronoUnit.SECONDS).isAfter(firstEmpty)) { + throw new EmptyStreamTimeoutException(String.format("Stream was empty for %d seconds consecutively", EMPTY_STREAM_TIMEOUT)); + } + } + continue; + } + emptyResponse = false; + try { + rawTweets.put(line); + } catch (Exception ignore) { + + } + } + } catch (Exception e) { + logger.error("Something went wrong. Closing stream... {}", e.getMessage()); + shutdown(); + } + } + } + + private class DeserializeTweetsTask implements Runnable { + + private final Logger logger = LoggerFactory.getLogger(DeserializeTweetsTask.class); + + @Override + public void run() { + while (isRunning) { + try { + String rawTweet = rawTweets.poll(POLL_WAIT, TimeUnit.MILLISECONDS); + if (rawTweet == null) continue; + StreamingTweetResponse tweet = StreamingTweetResponse.fromJson(rawTweet); + tweets.put(tweet); + } catch (InterruptedException ignore) { + + } catch (JsonProcessingException e) { + logger.debug("Json could not be parsed"); + } catch (Exception e) { + logger.debug("Exception in deserialization thread: ", e); + } + } + } + } + + private class TweetsListenersTask implements Runnable { + + private final Logger logger = LoggerFactory.getLogger(TweetsListenersTask.class); + @Override + public void run() { + processTweets(); + } + + private void processTweets() { + StreamingTweetResponse streamingTweet; + + while (isRunning) { + try { + streamingTweet = tweets.poll(POLL_WAIT, TimeUnit.MILLISECONDS); + if(streamingTweet == null) continue; + for (TweetsStreamListener listener : listeners) { + listener.onTweetArrival(streamingTweet); + } + } catch (InterruptedException e) { + + } + + } + + } + } +} + diff --git a/examples/src/main/java/com/twitter/clientlib/TweetsStreamListener.java b/src/main/java/com/twitter/clientlib/stream/TweetsStreamListener.java similarity index 73% rename from examples/src/main/java/com/twitter/clientlib/TweetsStreamListener.java rename to src/main/java/com/twitter/clientlib/stream/TweetsStreamListener.java index 5df0650..8b4e707 100644 --- a/examples/src/main/java/com/twitter/clientlib/TweetsStreamListener.java +++ b/src/main/java/com/twitter/clientlib/stream/TweetsStreamListener.java @@ -14,16 +14,13 @@ See the License for the specific language governing permissions and limitations under the License. -NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech). -https://openapi-generator.tech -Do not edit the class manually. */ -package com.twitter.clientlib; +package com.twitter.clientlib.stream; import com.twitter.clientlib.model.StreamingTweetResponse; public interface TweetsStreamListener { - void actionOnTweetsStream(StreamingTweetResponse streamingTweet); + void onTweetArrival(StreamingTweetResponse streamingTweet); } diff --git a/src/main/java/com/twitter/clientlib/stream/TwitterStream.java b/src/main/java/com/twitter/clientlib/stream/TwitterStream.java new file mode 100644 index 0000000..ac0f312 --- /dev/null +++ b/src/main/java/com/twitter/clientlib/stream/TwitterStream.java @@ -0,0 +1,133 @@ +/* +Copyright 2020 Twitter, Inc. +SPDX-License-Identifier: Apache-2.0 + +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 com.twitter.clientlib.stream; + +import com.twitter.clientlib.ApiClient; +import com.twitter.clientlib.ApiException; +import com.twitter.clientlib.TwitterCredentialsBearer; +import com.twitter.clientlib.api.TweetsApi; +import com.twitter.clientlib.exceptions.AuthenticationException; +import com.twitter.clientlib.exceptions.StreamException; +import com.twitter.clientlib.exceptions.TooManyRequestsException; +import com.twitter.clientlib.query.StreamQueryParameters; + +import okio.BufferedSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.PreDestroy; +import java.util.LinkedList; +import java.util.List; + +public class TwitterStream { + + private static final Logger logger = LoggerFactory.getLogger(TwitterStream.class); + private final TweetsApi tweets = new TweetsApi(); + + private final ApiClient apiClient = new ApiClient(); + + private final List listeners = new LinkedList<>(); + + private TweetsStreamExecutor executor; + + private int retries; + + public TwitterStream(TwitterCredentialsBearer credentials) { + init(); + apiClient.setTwitterCredentials(credentials); + } + + public TwitterStream(int retries) { + this.retries = retries; + init(); + } + + private void init() { + initBasePath(); + tweets.setClient(apiClient); + } + + /** + * Set retries + * @param retries the retries for reconnections on 429 status code errors + */ + public void setRetries(int retries) { + this.retries = retries; + } + + /** + * Add listener + * @param listener {@link TweetsStreamListener} for 'listening' the tweets returned from the stream + */ + public void addListener(TweetsStreamListener listener) { + listeners.add(listener); + } + + /** + * Remove listener + * @param listener {@link TweetsStreamListener} to be removed + */ + public void removeListener(TweetsStreamListener listener) { + listeners.remove(listener); + } + + /** + * Start streaming for sampleSearch + * @param streamParameters {@link StreamQueryParameters} the parameters for the request + * @return the results are returns through the listeners {@link #addListener(TweetsStreamListener)} + */ + public void startSampleStream(StreamQueryParameters streamParameters) { + try { + BufferedSource streamResult = tweets.sampleStream().parameters(streamParameters).execute(retries <= 0 ? 1 : retries); + executor = new TweetsStreamExecutor(streamResult); + listeners.forEach(executor::addListener); + executor.start(); + } catch (ApiException e) { + if(e.getCode() == 401) { + logger.error("Authentication didn't work"); + throw new AuthenticationException("Not authenticated. Please check the credentials", e); + } + if(e.getCode() == 429){ + /* for this error twitter indicates that to implement a reconnection mechanism + * see: https://developer.twitter.com/en/docs/twitter-api/tweets/volume-streams/integrate/handling-disconnections + */ + throw new TooManyRequestsException("Too many requests. Service responded with 429 status code. Consider setting 'retries' or increasing its value"); + } + throw new StreamException("An exception occurred during stream execution ",e); + } + } + + /** + * Closes the threads and performs any other required clean up + */ + public void shutdown() { + executor.shutdown(); + } + + @PreDestroy + public void preDestroy() { + executor.shutdown(); + } + + private void initBasePath() { + String basePath = System.getenv("TWITTER_API_BASE_PATH"); + apiClient.setBasePath(basePath != null ? basePath : "https://api.twitter.com"); + } + +} diff --git a/src/test/java/com/twitter/clientlib/api/TweetsApiTest.java b/src/test/java/com/twitter/clientlib/api/TweetsApiTest.java index f5e6893..cf7994e 100644 --- a/src/test/java/com/twitter/clientlib/api/TweetsApiTest.java +++ b/src/test/java/com/twitter/clientlib/api/TweetsApiTest.java @@ -58,6 +58,8 @@ import com.twitter.clientlib.model.UsersRetweetsCreateRequest; import com.twitter.clientlib.model.UsersRetweetsCreateResponse; import com.twitter.clientlib.model.UsersRetweetsDeleteResponse; +import com.twitter.clientlib.query.StreamQueryParameters; +import okio.BufferedSource; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -296,22 +298,7 @@ public void listsIdTweetsTest() throws ApiException { */ @Test public void sampleStreamTest() throws ApiException { - Integer backfillMinutes = null; - Set tweetFields = null; - Set expansions = null; - Set mediaFields = null; - Set pollFields = null; - Set userFields = null; - Set placeFields = null; - InputStream response = apiInstance.tweets().sampleStream() - .backfillMinutes(backfillMinutes) - .tweetFields(tweetFields) - .expansions(expansions) - .mediaFields(mediaFields) - .pollFields(pollFields) - .userFields(userFields) - .placeFields(placeFields) - .execute(); + BufferedSource response = apiInstance.tweets().sampleStream().execute(); // TODO: test validations } @@ -325,22 +312,7 @@ public void sampleStreamTest() throws ApiException { */ @Test public void searchStreamTest() throws ApiException { - Integer backfillMinutes = null; - Set tweetFields = null; - Set expansions = null; - Set mediaFields = null; - Set pollFields = null; - Set userFields = null; - Set placeFields = null; - InputStream response = apiInstance.tweets().searchStream() - .backfillMinutes(backfillMinutes) - .tweetFields(tweetFields) - .expansions(expansions) - .mediaFields(mediaFields) - .pollFields(pollFields) - .userFields(userFields) - .placeFields(placeFields) - .execute(); + BufferedSource response = apiInstance.tweets().searchStream().execute(); // TODO: test validations } diff --git a/src/test/java/com/twitter/clientlib/model/UrlEntityTest.java b/src/test/java/com/twitter/clientlib/model/UrlEntityTest.java index f011ad2..666ccac 100644 --- a/src/test/java/com/twitter/clientlib/model/UrlEntityTest.java +++ b/src/test/java/com/twitter/clientlib/model/UrlEntityTest.java @@ -22,21 +22,6 @@ package com.twitter.clientlib.model; -import com.google.gson.TypeAdapter; -import com.google.gson.annotations.JsonAdapter; -import com.google.gson.annotations.SerializedName; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; -import com.twitter.clientlib.model.EntityIndicesInclusiveExclusive; -import com.twitter.clientlib.model.UrlFields; -import com.twitter.clientlib.model.UrlImage; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; -import java.io.IOException; -import java.net.URL; -import java.util.ArrayList; -import java.util.List; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; diff --git a/src/test/java/com/twitter/clientlib/model/URLFieldsTest.java b/src/test/java/com/twitter/clientlib/model/UrlFieldsTest.java similarity index 82% rename from src/test/java/com/twitter/clientlib/model/URLFieldsTest.java rename to src/test/java/com/twitter/clientlib/model/UrlFieldsTest.java index 5fd62e4..699f44f 100644 --- a/src/test/java/com/twitter/clientlib/model/URLFieldsTest.java +++ b/src/test/java/com/twitter/clientlib/model/UrlFieldsTest.java @@ -22,19 +22,6 @@ package com.twitter.clientlib.model; -import com.google.gson.TypeAdapter; -import com.google.gson.annotations.JsonAdapter; -import com.google.gson.annotations.SerializedName; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; -import com.twitter.clientlib.model.UrlImage; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; -import java.io.IOException; -import java.net.URL; -import java.util.ArrayList; -import java.util.List; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; diff --git a/src/test/java/com/twitter/clientlib/model/URLImageTest.java b/src/test/java/com/twitter/clientlib/model/UrlImageTest.java similarity index 78% rename from src/test/java/com/twitter/clientlib/model/URLImageTest.java rename to src/test/java/com/twitter/clientlib/model/UrlImageTest.java index 27deedf..41984a2 100644 --- a/src/test/java/com/twitter/clientlib/model/URLImageTest.java +++ b/src/test/java/com/twitter/clientlib/model/UrlImageTest.java @@ -22,16 +22,6 @@ package com.twitter.clientlib.model; -import com.google.gson.TypeAdapter; -import com.google.gson.annotations.JsonAdapter; -import com.google.gson.annotations.SerializedName; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; -import io.swagger.annotations.ApiModel; -import io.swagger.annotations.ApiModelProperty; -import java.io.IOException; -import java.net.URL; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; diff --git a/src/test/java/com/twitter/clientlib/stream/TwitterStreamTest.java b/src/test/java/com/twitter/clientlib/stream/TwitterStreamTest.java new file mode 100644 index 0000000..d41c35f --- /dev/null +++ b/src/test/java/com/twitter/clientlib/stream/TwitterStreamTest.java @@ -0,0 +1,115 @@ +/* +Copyright 2020 Twitter, Inc. +SPDX-License-Identifier: Apache-2.0 + +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 com.twitter.clientlib.stream; + +import com.twitter.clientlib.TwitterCredentialsBearer; +import com.twitter.clientlib.integration.ApiTester; +import com.twitter.clientlib.model.StreamingTweetResponse; +import com.twitter.clientlib.query.StreamQueryParameters; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import static org.junit.jupiter.api.Assertions.*; + +@Disabled +class TwitterStreamTest extends ApiTester { + + private static TwitterStream twitterStream; + + private TweetsStreamListener courier; + private List tweets; + + @BeforeAll + public static void beforeAll() { + twitterStream = new TwitterStream(new TwitterCredentialsBearer(System.getenv("TWITTER_BEARER_TOKEN"))); + twitterStream.setRetries(4); + } + + @BeforeEach + public void setUp() { + tweets = new LinkedList<>(); + courier = new TweetCourier(tweets); + twitterStream.addListener(courier); + } + + @AfterEach + public void tearDown() { + twitterStream.removeListener(courier); + twitterStream.shutdown(); + } + + @Test + public void shutdown() throws InterruptedException { + twitterStream.startSampleStream(new StreamQueryParameters.Builder().build()); + TimeUnit.SECONDS.sleep(5); + assertTrue(tweets.size() > 1); + twitterStream.shutdown(); + } + + @Test + public void sampleStream() throws InterruptedException { + twitterStream.startSampleStream(new StreamQueryParameters.Builder().build()); + TimeUnit.SECONDS.sleep(5); + assertTrue(tweets.size() > 1); + } + + @Test + public void sampleStreamAddSecondListener() throws InterruptedException { + List tweetsDuplicate = new LinkedList<>(); + TweetsStreamListener courierDuplicate = new TweetCourier(tweetsDuplicate); + twitterStream.addListener(courierDuplicate); + twitterStream.startSampleStream(new StreamQueryParameters.Builder().build()); + TimeUnit.SECONDS.sleep(5); + assertTrue(tweets.size() > 1); + assertTrue(tweetsDuplicate.size() > 1); + assertEquals(tweets.size(), tweetsDuplicate.size()); + } + + @Test + public void sampleStreamAddSecondListenerAndRemove() throws InterruptedException { + List tweetsDuplicate = new LinkedList<>(); + TweetsStreamListener courierDuplicate = new TweetCourier(tweetsDuplicate); + twitterStream.addListener(courierDuplicate); + twitterStream.removeListener(courierDuplicate); + twitterStream.startSampleStream(new StreamQueryParameters.Builder().build()); + TimeUnit.SECONDS.sleep(5); + assertTrue(tweets.size() > 1); + assertEquals(0, tweetsDuplicate.size()); + } + + private class TweetCourier implements TweetsStreamListener { + + private List tweetsList; + + private TweetCourier(List tweetsList) { + this.tweetsList = tweetsList; + } + @Override + public void onTweetArrival(StreamingTweetResponse tweet) { + tweetsList.add(tweet); + } + } +} \ No newline at end of file