Skip to content

Commit d63dc3b

Browse files
committed
add images to twitter publisher service
1 parent 183f0cf commit d63dc3b

5 files changed

Lines changed: 165 additions & 36 deletions

File tree

src/main/java/com/mastercloudapps/twitterscheduler/application/model/twitter/PublishTweetRequest.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package com.mastercloudapps.twitterscheduler.application.model.twitter;
22

3+
import java.util.List;
4+
35
import lombok.Builder;
46
import lombok.EqualsAndHashCode;
57
import lombok.Getter;
@@ -12,5 +14,7 @@
1214
public class PublishTweetRequest {
1315

1416
private String message;
17+
18+
private List<String> images;
1519

1620
}

src/main/java/com/mastercloudapps/twitterscheduler/application/model/twitter/PublishTweetResponse.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.mastercloudapps.twitterscheduler.application.model.twitter;
22

33
import java.time.Instant;
4+
import java.util.List;
45

56
import lombok.Builder;
67
import lombok.EqualsAndHashCode;
@@ -20,4 +21,6 @@ public class PublishTweetResponse {
2021
private String url;
2122

2223
private Instant publishedAt;
24+
25+
private List<PublishedImageResponse> images;
2326
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.mastercloudapps.twitterscheduler.application.model.twitter;
2+
3+
import lombok.Builder;
4+
import lombok.EqualsAndHashCode;
5+
import lombok.Getter;
6+
import lombok.ToString;
7+
8+
@Getter
9+
@EqualsAndHashCode
10+
@ToString
11+
@Builder
12+
public class PublishedImageResponse {
13+
14+
private Long id;
15+
16+
private Long size;
17+
18+
private String type;
19+
20+
private Integer width;
21+
22+
private Integer height;
23+
}

src/main/java/com/mastercloudapps/twitterscheduler/application/service/PublisherService.java

Lines changed: 66 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,22 @@
1010
import org.slf4j.Logger;
1111
import org.slf4j.LoggerFactory;
1212
import org.springframework.stereotype.Component;
13+
import org.togglz.core.manager.FeatureManager;
1314

1415
import com.mastercloudapps.twitterscheduler.application.model.operation.PublishPendingTweetOnDemandOperation;
1516
import com.mastercloudapps.twitterscheduler.application.model.scheduler.SchedulerConfiguration;
1617
import com.mastercloudapps.twitterscheduler.application.model.twitter.PublishTweetRequest;
18+
import com.mastercloudapps.twitterscheduler.application.model.twitter.PublishTweetResponse;
1719
import com.mastercloudapps.twitterscheduler.application.service.twitter.TwitterService;
1820
import com.mastercloudapps.twitterscheduler.application.usecase.PublishPendingTweetOnDemandUseCase;
1921
import com.mastercloudapps.twitterscheduler.application.usecase.PublishPendingTweetsUseCase;
22+
import com.mastercloudapps.twitterscheduler.configuration.featureflags.Features;
2023
import com.mastercloudapps.twitterscheduler.domain.pending.PendingTweet;
2124
import com.mastercloudapps.twitterscheduler.domain.pending.PendingTweetPort;
2225
import com.mastercloudapps.twitterscheduler.domain.shared.NullableInstant;
2326
import com.mastercloudapps.twitterscheduler.domain.tweet.PublicationType;
2427
import com.mastercloudapps.twitterscheduler.domain.tweet.Tweet;
28+
import com.mastercloudapps.twitterscheduler.domain.tweet.TweetImage;
2529
import com.mastercloudapps.twitterscheduler.domain.tweet.TweetPort;
2630

2731
@Component
@@ -36,15 +40,18 @@ public class PublisherService implements PublishPendingTweetsUseCase, PublishPen
3640
private PendingTweetPort pendingTweetPort;
3741

3842
private TweetPort tweetPort;
43+
44+
private FeatureManager featureManager;
3945

4046
public PublisherService(final SchedulerConfiguration schedulerConfiguration,
4147
final TwitterService twitterService, final PendingTweetPort pendingTweetPort,
42-
final TweetPort tweetPort) {
48+
final TweetPort tweetPort, final FeatureManager featureManager) {
4349

4450
this.schedulerConfiguration = schedulerConfiguration;
4551
this.twitterService = twitterService;
4652
this.pendingTweetPort = pendingTweetPort;
4753
this.tweetPort = tweetPort;
54+
this.featureManager = featureManager;
4855
}
4956

5057
@Override
@@ -59,25 +66,18 @@ public void publishPending() {
5966
logger.info("Recovered " + pendingTweets.size() + " pending tweets to publish");
6067

6168
pendingTweets.forEach(pending -> {
62-
63-
final var publishedTweet = twitterService.publish(PublishTweetRequest.builder()
64-
.message(pending.message().message())
65-
.build());
69+
70+
final var publishedTweet = twitterService.publish(this.buildPublishTweetRequest(pending));
6671

6772
logger.info("Successful scheduled publication for pending tweet with id = " + pending.id().id());
73+
6874
publishedTweet.ifPresent(published -> {
6975

7076
pendingTweetPort.delete(pending.id().id());
71-
72-
tweetPort.create(Tweet.builder()
73-
.id(published.getId())
74-
.message(published.getMessage())
75-
.url(published.getUrl())
76-
.requestedPublicationDate(pending.publicationDate().instant())
77-
.publishedAt(published.getPublishedAt())
78-
.createdAt(NullableInstant.now().instant())
79-
.publicationType(PublicationType.SCHEDULED)
80-
.build());
77+
78+
tweetPort.create(this
79+
.buildTweet(publishedTweet.get(), pending, PublicationType.SCHEDULED));
80+
8181
});
8282
});
8383
}
@@ -90,29 +90,66 @@ public Optional<Tweet> publishImmediatly(PublishPendingTweetOnDemandOperation op
9090
if (pending.isPresent()) {
9191

9292
logger.info("Found pending tweet with id = " + pending.get().id() + " to publish on demand");
93-
final var publishedTweet = twitterService.publish(PublishTweetRequest.builder()
94-
.message(pending.get().message().message())
95-
.build());
93+
94+
final var publishedTweet = twitterService.publish(this.buildPublishTweetRequest(pending.get()));
9695

97-
logger.info("Successful on demand publication for pending tweet with id = " + pending.get().id().id());
9896
if (publishedTweet.isPresent()) {
99-
97+
98+
logger.info("Successful on demand publication for pending tweet with id = " + pending.get().id().id()
99+
+ "tweetId is " + publishedTweet.get().getId());
100+
100101
pendingTweetPort.delete(pending.get().id().id());
101102

102-
final var tweet = tweetPort.create(Tweet.builder()
103-
.id(publishedTweet.get().getId())
104-
.message(publishedTweet.get().getMessage())
105-
.url(publishedTweet.get().getUrl())
106-
.requestedPublicationDate(pending.get().publicationDate().instant())
107-
.publishedAt(publishedTweet.get().getPublishedAt())
108-
.createdAt(NullableInstant.now().instant())
109-
.publicationType(PublicationType.ON_DEMAND)
110-
.build());
103+
final var tweet = tweetPort.create(this
104+
.buildTweet(publishedTweet.get(), pending.get(), PublicationType.ON_DEMAND));
111105

112106
return Optional.of(tweet);
113107
}
114108
}
115109
return Optional.empty();
116110
}
111+
112+
private PublishTweetRequest buildPublishTweetRequest(PendingTweet pendingTweet) {
113+
114+
final var builder = PublishTweetRequest.builder()
115+
.message(pendingTweet.message().message());
116+
117+
if(featureManager.isActive(Features.TWEETS_WITH_IMAGES)) {
118+
Optional.ofNullable(pendingTweet.getImages())
119+
.ifPresent(images -> {
120+
builder.images(images.stream()
121+
.map(image -> image.url().url())
122+
.collect(Collectors.toList()));
123+
});
124+
}
125+
126+
return builder.build();
127+
}
128+
129+
private Tweet buildTweet(PublishTweetResponse publishTweetResponse, PendingTweet pendingTweet, PublicationType publicationType) {
130+
131+
final var builder = Tweet.builder()
132+
.id(publishTweetResponse.getId())
133+
.message(publishTweetResponse.getMessage())
134+
.url(publishTweetResponse.getUrl())
135+
.requestedPublicationDate(pendingTweet.publicationDate().instant())
136+
.publishedAt(publishTweetResponse.getPublishedAt())
137+
.createdAt(NullableInstant.now().instant())
138+
.publicationType(publicationType);
139+
140+
if (!publishTweetResponse.getImages().isEmpty()) {
141+
builder.images(publishTweetResponse.getImages().stream()
142+
.map(image -> TweetImage.builder()
143+
.id(image.getId())
144+
.size(image.getSize())
145+
.type(image.getType())
146+
.width(image.getWidth())
147+
.height(image.getHeight())
148+
.build())
149+
.collect(Collectors.toList()));
150+
}
151+
152+
return builder.build();
153+
}
117154

118155
}

src/main/java/com/mastercloudapps/twitterscheduler/application/service/twitter/TwitterServiceImpl.java

Lines changed: 69 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,29 @@
11
package com.mastercloudapps.twitterscheduler.application.service.twitter;
22

3+
import java.io.IOException;
4+
import java.io.InputStream;
5+
import java.net.MalformedURLException;
6+
import java.net.URL;
7+
import java.util.ArrayList;
8+
import java.util.Collections;
9+
import java.util.List;
310
import java.util.Optional;
11+
import java.util.UUID;
12+
import java.util.stream.Collectors;
413

514
import org.springframework.beans.factory.annotation.Autowired;
615
import org.springframework.stereotype.Component;
716

817
import com.mastercloudapps.twitterscheduler.application.model.twitter.PublishTweetRequest;
918
import com.mastercloudapps.twitterscheduler.application.model.twitter.PublishTweetResponse;
19+
import com.mastercloudapps.twitterscheduler.application.model.twitter.PublishedImageResponse;
1020
import com.mastercloudapps.twitterscheduler.configuration.TwitterConfiguration;
1121
import com.mastercloudapps.twitterscheduler.domain.exception.ServiceException;
1222

1323
import twitter4j.Status;
1424
import twitter4j.StatusUpdate;
25+
import twitter4j.TwitterException;
26+
import twitter4j.UploadedMedia;
1527

1628
@Component
1729
public class TwitterServiceImpl implements TwitterService {
@@ -30,18 +42,26 @@ public TwitterServiceImpl(TwitterConfiguration twitter) {
3042
public Optional<PublishTweetResponse> publish(PublishTweetRequest request) {
3143

3244
try {
33-
45+
3446
StatusUpdate statusUpdate = new StatusUpdate(request.getMessage());
47+
List<UploadedMedia> uploadedImages = Collections.emptyList();
48+
49+
if (!request.getImages().isEmpty()) {
50+
uploadedImages = this.uploadImages(request);
51+
statusUpdate.setMediaIds(this.getImageIds(uploadedImages));
52+
}
53+
3554
Status status = this.twitter.getClient().updateStatus(statusUpdate);
3655

37-
PublishTweetResponse response = PublishTweetResponse.builder()
56+
final var responseBuilder = PublishTweetResponse.builder()
3857
.id(status.getId())
3958
.url(this.getTweetUrl(status))
4059
.message(statusUpdate.getStatus())
41-
.publishedAt(status.getCreatedAt().toInstant())
42-
.build();
60+
.publishedAt(status.getCreatedAt().toInstant());
61+
62+
responseBuilder.images(this.getImages(uploadedImages));
4363

44-
return Optional.of(response);
64+
return Optional.of(responseBuilder.build());
4565

4666
} catch (Exception e) {
4767
throw new ServiceException(ERR_MSG_PUBLISH_TWEET, e);
@@ -50,7 +70,49 @@ public Optional<PublishTweetResponse> publish(PublishTweetRequest request) {
5070

5171
private String getTweetUrl(Status status) {
5272

53-
return "https://twitter.com/" + status.getUser().getScreenName() + "/status/" + status.getId();
73+
return "https://twitter.com/" + status.getUser().getScreenName()
74+
+ "/status/" + status.getId();
75+
}
76+
77+
private List<UploadedMedia> uploadImages(PublishTweetRequest request)
78+
throws MalformedURLException, IOException, TwitterException {
79+
80+
List<UploadedMedia> uploadedImages = new ArrayList<>();
81+
for (int i=0; i<request.getImages().size(); i++) {
82+
InputStream imgInputStream = new URL(request.getImages().get(i)).openStream();
83+
UploadedMedia uploadedImage = this.twitter.getClient()
84+
.uploadMedia(UUID.randomUUID() + ".jpg", imgInputStream);
85+
uploadedImages.add(uploadedImage);
86+
}
87+
return uploadedImages;
88+
}
89+
90+
private long[] getImageIds(List<UploadedMedia> uploadedImages) {
91+
92+
long[] uploadedImagesIds = new long[uploadedImages.size()];
93+
for (int i = 0; i<uploadedImages.size(); i++) {
94+
uploadedImagesIds[i] = uploadedImages.get(i).getMediaId();
95+
}
96+
return uploadedImagesIds;
5497
}
5598

56-
}
99+
private List<PublishedImageResponse> getImages(List<UploadedMedia> uploadedImages) {
100+
101+
List<PublishedImageResponse> images;
102+
if (!uploadedImages.isEmpty()) {
103+
images = uploadedImages.stream()
104+
.map(image -> PublishedImageResponse.builder()
105+
.id(image.getMediaId())
106+
.size(image.getSize())
107+
.type(image.getImageType())
108+
.width(image.getImageWidth())
109+
.height(image.getImageHeight())
110+
.build())
111+
.collect(Collectors.toList());
112+
} else {
113+
images = Collections.emptyList();
114+
}
115+
return images;
116+
}
117+
118+
}

0 commit comments

Comments
 (0)