Skip to content
This repository was archived by the owner on Nov 6, 2023. It is now read-only.

[Arqutectura Hexagonal Práctica]:feat:Integration con alguna API #23

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
32 changes: 32 additions & 0 deletions src/mooc/build.gradle
Original file line number Diff line number Diff line change
@@ -1,3 +1,35 @@
ext {
mapstructVersion = "1.5.0.RC1"
}

test {
useJUnitPlatform()
}

dependencies {
compileOnly 'org.projectlombok:lombok:1.18.24'
annotationProcessor 'org.projectlombok:lombok:1.18.24'
testCompileOnly 'org.projectlombok:lombok:1.18.24'
testAnnotationProcessor 'org.projectlombok:lombok:1.18.24'
implementation "org.mapstruct:mapstruct:${mapstructVersion}"
annotationProcessor "org.mapstruct:mapstruct-processor:${mapstructVersion}"
testAnnotationProcessor "org.mapstruct:mapstruct-processor:${mapstructVersion}"
implementation 'org.apache.logging.log4j:log4j-api:2.17.2'
implementation 'org.apache.logging.log4j:log4j-core:2.17.2'
implementation group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.13.2.2'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
testImplementation 'org.mockito:mockito-core:2.21.0'
testImplementation 'org.mockito:mockito-junit-jupiter:2.23.0'
testImplementation 'org.assertj:assertj-core:3.22.0'
implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.0'
compile project(":src:shared")
}

tasks.withType(JavaCompile) {
options.compilerArgs = [
'-Amapstruct.suppressGeneratorTimestamp=true',
'-Amapstruct.suppressGeneratorVersionInfoComment=true',
'-Amapstruct.unmappedTargetPolicy=ERROR'
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ public void consume(VideoPublished event) {
System.out.println(
String.format(
"Hey! There is a new video with title <%s> and description <%s>",
event.title(),
event.description()
event.getTitle(),
event.getDescription()
)
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@
import tv.codely.mooc.video.domain.Video;
import tv.codely.mooc.video.domain.VideoDescription;
import tv.codely.mooc.video.domain.VideoTitle;
import tv.codely.mooc.video.domain.port.VideoManagementPort;
import tv.codely.shared.domain.EventBus;

public final class VideoPublisher {
private final EventBus eventBus;
private final VideoManagementPort videoManagementPort;

public VideoPublisher(EventBus eventBus) {
public VideoPublisher(EventBus eventBus, VideoManagementPort videoManagementPort) {
this.eventBus = eventBus;
this.videoManagementPort = videoManagementPort;
}

public void publish(String rawTitle, String rawDescription) {
Expand All @@ -19,5 +22,8 @@ public void publish(String rawTitle, String rawDescription) {
final var video = Video.publish(title, description);

eventBus.publish(video.pullDomainEvents());

// Publicar el vídeo sobre META, aunque no sea algo realmente funcional es para llevar a cabo el ejercicio
videoManagementPort.publish(video);
}
}
15 changes: 9 additions & 6 deletions src/mooc/main/tv/codely/mooc/video/domain/Video.java
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
package tv.codely.mooc.video.domain;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import tv.codely.shared.domain.AggregateRoot;

@Builder
@Data
@EqualsAndHashCode(callSuper = false)
@AllArgsConstructor
public final class Video extends AggregateRoot {
private final VideoTitle title;
private final VideoDescription description;

private Video(VideoTitle title, VideoDescription description) {
this.title = title;
this.description = description;
}

public static Video publish(VideoTitle title, VideoDescription description) {
var video = new Video(title, description);

var videoCreated = new VideoPublished(title.value(), description.value());
var videoCreated = new VideoPublished(title.getValue(), description.getValue());

video.record(videoCreated);

Expand Down
31 changes: 8 additions & 23 deletions src/mooc/main/tv/codely/mooc/video/domain/VideoDescription.java
Original file line number Diff line number Diff line change
@@ -1,28 +1,13 @@
package tv.codely.mooc.video.domain;

public final class VideoDescription {
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;

@Builder
@Data
@AllArgsConstructor
public class VideoDescription {
private final String value;

public VideoDescription(final String value) {
this.value = value;
}

public String value() {
return value;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;

VideoDescription that = (VideoDescription) o;

return value.equals(that.value);
}

@Override
public int hashCode() {
return value.hashCode();
}
}
29 changes: 4 additions & 25 deletions src/mooc/main/tv/codely/mooc/video/domain/VideoPublished.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package tv.codely.mooc.video.domain;

import lombok.Builder;
import lombok.Data;
import tv.codely.shared.domain.DomainEvent;

@Builder
@Data
public final class VideoPublished implements DomainEvent {
private static final String FULL_QUALIFIED_EVENT_NAME = "codelytv.video.video.event.1.video.published";

Expand All @@ -17,29 +21,4 @@ public String fullQualifiedEventName() {
return FULL_QUALIFIED_EVENT_NAME;
}

public String title() {
return title;
}

public String description() {
return description;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;

VideoPublished that = (VideoPublished) o;

if (!title.equals(that.title)) return false;
return description.equals(that.description);
}

@Override
public int hashCode() {
int result = title.hashCode();
result = 31 * result + description.hashCode();
return result;
}
}
29 changes: 7 additions & 22 deletions src/mooc/main/tv/codely/mooc/video/domain/VideoTitle.java
Original file line number Diff line number Diff line change
@@ -1,28 +1,13 @@
package tv.codely.mooc.video.domain;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;

@Builder
@Data
@AllArgsConstructor
public final class VideoTitle {
private final String value;

public VideoTitle(String value) {
this.value = value;
}

public String value() {
return value;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;

VideoTitle that = (VideoTitle) o;

return value.equals(that.value);
}

@Override
public int hashCode() {
return value.hashCode();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package tv.codely.mooc.video.domain.exception;

public class RuntimeServiceException extends RuntimeException {

public RuntimeServiceException(final String message) {
super(message);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package tv.codely.mooc.video.domain.port;

import tv.codely.mooc.video.domain.Video;

public interface VideoManagementPort {

void publish(final Video video);

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import tv.codely.mooc.notification.application.create.SendPushToSubscribersOnVideoPublished;
import tv.codely.mooc.video.application.publish.VideoPublisher;
import tv.codely.mooc.video.domain.port.VideoManagementPort;
import tv.codely.mooc.video.infrastructure.adapter.MetaVideoManagementAdapter;
import tv.codely.shared.application.DomainEventSubscriber;
import tv.codely.shared.domain.EventBus;
import tv.codely.shared.infrastructure.bus.ReactorEventBus;
Expand All @@ -14,7 +16,8 @@ public static void main(String[] args) {
new SendPushToSubscribersOnVideoPublished()
);
final EventBus eventBus = new ReactorEventBus(subscribers);
final var videoPublisher = new VideoPublisher(eventBus);
final VideoManagementPort videoManagementPort = new MetaVideoManagementAdapter();
final var videoPublisher = new VideoPublisher(eventBus, videoManagementPort);

final var videoTitle = "\uD83C\uDF89 New YouTube.com/CodelyTV video title";
final var videoDescription = "This should be the video description \uD83D\uDE42";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package tv.codely.mooc.video.infrastructure.adapter;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.Setter;
import lombok.extern.log4j.Log4j2;
import tv.codely.mooc.video.domain.Video;
import tv.codely.mooc.video.domain.exception.RuntimeServiceException;
import tv.codely.mooc.video.domain.port.VideoManagementPort;
import tv.codely.mooc.video.infrastructure.dto.youtube.VideoResourceDTO;

import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Objects;

@Log4j2
public class MetaVideoManagementAdapter implements VideoManagementPort {

private static final String YOUTUBE_UPLOAD_URL = "https://youtube.googleapis.com/youtube/v3/videos?part=requestPArt&autoLevels=true&uploadType=mp4&key=TokeApiKey";
private static final String YOUTUBE_API_ACCESS_TOKEN = "BearerTokeApiKey";
private static final String HEADER_AUTHORIZATION = "Authorization";
private static final String HEADER_ACCEPT = "Accept";
private static final String HEADER_CONTENT_TYPE = "Content-Type";
private static final String POST_METHOD = "POST";
private static final String APPLICATION_JSON = "application/json";

@Setter
private HttpURLConnection connection;

@Override
public void publish(final Video video) {

validateParameters(video);

try {
final URL url = new URL(YOUTUBE_UPLOAD_URL);
connection = getHttpURLConnection(url);
final String bodyJson = retrieveBodyJson(video);

injectBodyInConnection(connection, bodyJson);

if (connection.getResponseCode() != 200) {
log.error("Failed : HTTP error code : " + connection.getResponseCode());
throw new RuntimeServiceException("Error connection");
}

connection.disconnect();
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}

private void validateParameters(final Video video) {
if (Objects.isNull(video)
|| Objects.isNull(video.getTitle())
|| Objects.isNull(video.getDescription())) {
throw new RuntimeServiceException("The video is empty");
}
}

private void injectBodyInConnection(final HttpURLConnection conn, final String bodyJson) throws IOException {
OutputStream os = conn.getOutputStream();
os.write(bodyJson.getBytes());
os.flush();
}

private String retrieveBodyJson(final Video video) throws JsonProcessingException {
final VideoResourceDTO videoResourceDTO = mapVideoToVideResource(video);
return new ObjectMapper().writeValueAsString(videoResourceDTO);
}

private VideoResourceDTO mapVideoToVideResource(final Video video) {
return VideoResourceDTO.builder()
.fileDetails(VideoResourceDTO.FileDetailsDTO.builder()
.name(video.getTitle().getValue())
.container(video.getDescription().getValue())
.build())
.build();
}

private HttpURLConnection getHttpURLConnection(final URL url) throws IOException {
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod(POST_METHOD);
conn.setRequestProperty(HEADER_AUTHORIZATION, YOUTUBE_API_ACCESS_TOKEN);
conn.setRequestProperty(HEADER_ACCEPT, APPLICATION_JSON);
conn.setRequestProperty(HEADER_CONTENT_TYPE, APPLICATION_JSON);
return conn;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package tv.codely.mooc.video.infrastructure.dto.youtube;

import lombok.Builder;
import lombok.Data;

@Data
@Builder
public class VideoResourceDTO {
private FileDetailsDTO fileDetails;

@Data
@Builder
public static class FileDetailsDTO {
private String name;
private String container;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,21 @@

import org.junit.jupiter.api.Test;
import tv.codely.mooc.video.domain.VideoPublished;
import tv.codely.mooc.video.domain.port.VideoManagementPort;
import tv.codely.shared.domain.EventBus;

import java.util.List;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

final class VideoPublisherShould {
final class VideoPublisherShouldTest {

@Test
void publish_the_video_published_domain_event() {
final EventBus eventBus = mock(EventBus.class);
final var videoPublisher = new VideoPublisher(eventBus);
final VideoManagementPort videoManagementPort = mock(VideoManagementPort.class);
final var videoPublisher = new VideoPublisher(eventBus, videoManagementPort);

final var videoTitle = "\uD83C\uDF89 New YouTube.com/CodelyTV video title";
final var videoDescription = "This should be the video description \uD83D\uDE42";
Expand Down
Loading