Skip to content

Commit

Permalink
Release 0.2.0 (#32)
Browse files Browse the repository at this point in the history
  • Loading branch information
levimdmiller authored Feb 18, 2021
1 parent 9f56731 commit 908bf77
Show file tree
Hide file tree
Showing 30 changed files with 786 additions and 10 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
</parent>
<groupId>ca.levimiller</groupId>
<artifactId>sms-bridge</artifactId>
<version>0.1.0</version>
<version>0.2.0</version>
<name>sms-bridge</name>
<description>Sms Bridge for Matrix</description>

Expand Down
14 changes: 14 additions & 0 deletions src/main/java/ca/levimiller/smsbridge/data/db/MediaRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package ca.levimiller.smsbridge.data.db;

import ca.levimiller.smsbridge.data.model.Media;
import java.util.Optional;
import java.util.UUID;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.querydsl.QuerydslPredicateExecutor;
import org.springframework.stereotype.Repository;

@Repository
public interface MediaRepository extends JpaRepository<Media, Long>,
QuerydslPredicateExecutor<Media> {
Optional<Media> findDistinctByUid(UUID uuid);
}
21 changes: 19 additions & 2 deletions src/main/java/ca/levimiller/smsbridge/data/model/Media.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package ca.levimiller.smsbridge.data.model;

import java.util.Objects;
import java.util.UUID;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.PrePersist;
import javax.persistence.Table;
import javax.validation.constraints.Size;
import lombok.AllArgsConstructor;
Expand All @@ -27,6 +29,9 @@
@Where(clause = "deleted = false")
public class Media extends BaseModel {

@Column(name = "uid", unique = true)
private UUID uid;

@Size(max = 255)
@Column(name = "url")
private String url;
Expand All @@ -39,6 +44,16 @@ public class Media extends BaseModel {
@JoinColumn(name = "message_id")
private Message message;

/**
* Auto generate uuid if missing.
*/
@PrePersist
public void autofill() {
if (uid == null) {
uid = UUID.randomUUID();
}
}

@Override
public boolean equals(Object o) {
if (this == o) {
Expand All @@ -57,21 +72,23 @@ public boolean equals(Object o) {
if (message == null) {
return true;
}
return Objects.equals(url, media.url)
return Objects.equals(uid, media.uid)
&& Objects.equals(url, media.url)
&& Objects.equals(contentType, media.contentType)
// break infinite loop
&& Objects.equals(message.getId(), media.message.getId());
}

@Override
public int hashCode() {
return Objects.hash(super.hashCode(), url, contentType,
return Objects.hash(super.hashCode(), uid, url, contentType,
message == null ? null : message.getId());
}

@Override
public String toString() {
return "Media{"
+ "uid='" + uid + '\''
+ "url='" + url + '\''
+ ", contentType='" + contentType + '\''
+ ", message=" + (message == null ? null : message.getId()) // break infinite loop
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package ca.levimiller.smsbridge.data.transformer.matrix;

import ca.levimiller.smsbridge.data.model.Media;
import io.github.ma1uta.matrix.event.content.RoomMessageContent;
import io.github.ma1uta.matrix.event.message.Audio;
import io.github.ma1uta.matrix.event.message.File;
import io.github.ma1uta.matrix.event.message.Image;
import io.github.ma1uta.matrix.event.message.Video;
import java.util.Collections;
import java.util.List;
import org.springframework.stereotype.Component;

@Component
public class MatrixMediaTransformer {

List<Media> transform(RoomMessageContent content) {
String url = null;
String contentType = null;

// Mapstruct doesn't support polymorphism :(
if (content instanceof Audio) {
Audio audio = (Audio) content;
url = audio.getUrl();
contentType = audio.getInfo().getMimetype();
} else if (content instanceof File) {
File file = (File) content;
url = file.getUrl();
contentType = file.getInfo().getMimetype();
} else if (content instanceof Image) {
Image image = (Image) content;
url = image.getUrl();
contentType = image.getInfo().getMimetype();
} else if (content instanceof Video) {
Video video = (Video) content;
url = video.getUrl();
contentType = video.getInfo().getMimetype();
}

return url == null ? Collections.emptyList()
: Collections.singletonList(Media.builder()
.url(url)
.contentType(contentType)
.build());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,19 @@
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;

@Mapper(componentModel = "spring", uses = {MatrixContactTransformer.class})
@Mapper(
componentModel = "spring",
uses = {
MatrixContactTransformer.class,
MatrixMediaTransformer.class
}
)
public interface MatrixRoomMessageTransformer {

@Mapping(source = "eventId", target = "uid")
@Mapping(source = "content.body", target = "body")
@Mapping(source = "roomId", target = "toContact", qualifiedBy = To.class)
@Mapping(source = "sender", target = "fromContact", qualifiedBy = From.class)
@Mapping(source = "content", target = "media")
Message transform(RoomMessage<RoomMessageContent> roomMessage) throws TransformationException;
}
12 changes: 12 additions & 0 deletions src/main/java/ca/levimiller/smsbridge/rest/TwilioController.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import java.util.UUID;
import javax.validation.Valid;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
Expand All @@ -26,4 +31,11 @@ public interface TwilioController {
@ApiResponse(code = 201, message = "Created", response = String.class),
@ApiResponse(code = 400, message = "Request not valid")})
void createSms(@Valid @RequestBody TwilioSmsDto sms);

@GetMapping("/attachment/{media_uid}")
@ApiOperation("Returns the attachment file")
@ApiResponses(value = {
@ApiResponse(code = 200, message = "Success", response = String.class),
@ApiResponse(code = 400, message = "Request not valid")})
ResponseEntity<InputStreamResource> getAttachment(@PathVariable("media_uid") UUID mediaUid);
}
32 changes: 31 additions & 1 deletion src/main/java/ca/levimiller/smsbridge/rest/impl/TwilioApi.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,24 @@
package ca.levimiller.smsbridge.rest.impl;

import ca.levimiller.smsbridge.data.db.MediaRepository;
import ca.levimiller.smsbridge.data.dto.TwilioSmsDto;
import ca.levimiller.smsbridge.data.model.Media;
import ca.levimiller.smsbridge.data.model.Message;
import ca.levimiller.smsbridge.data.transformer.twilio.MessageTransformer;
import ca.levimiller.smsbridge.error.NotFoundException;
import ca.levimiller.smsbridge.rest.TwilioController;
import ca.levimiller.smsbridge.service.ChatService;
import ca.levimiller.smsbridge.service.FileService;
import ca.levimiller.smsbridge.service.MessageService;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.UUID;
import javax.inject.Inject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.io.InputStreamResource;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;

@Slf4j
Expand All @@ -18,16 +28,22 @@ public class TwilioApi implements TwilioController {
private final ChatService chatService;
private final MessageTransformer messageTransformer;
private final MessageService messageService;
private final MediaRepository mediaRepository;
private final FileService fileService;

@Inject
public TwilioApi(
@Qualifier("matrixChatService")
ChatService chatService,
MessageTransformer messageTransformer,
MessageService messageService) {
MessageService messageService,
MediaRepository mediaRepository,
@Qualifier("matrixFileService") FileService fileService) {
this.chatService = chatService;
this.messageTransformer = messageTransformer;
this.messageService = messageService;
this.mediaRepository = mediaRepository;
this.fileService = fileService;
}

@Override
Expand All @@ -40,4 +56,18 @@ public void createSms(TwilioSmsDto sms) {
messageService.save(message);
chatService.sendMessage(message);
}

@Override
public ResponseEntity<InputStreamResource> getAttachment(UUID mediaUid) {
Media media = mediaRepository.findDistinctByUid(mediaUid)
.orElseThrow(() -> new NotFoundException("Requested attachment not found."));
try {
return ResponseEntity.ok()
.contentType(MediaType.valueOf(media.getContentType()))
.body(new InputStreamResource(fileService.getFileStream(media.getUrl())));
} catch (URISyntaxException | IOException e) {
log.error("Error getting attachment from matrix", e);
throw new NotFoundException("Media url malformed or doesn't exist.");
}
}
}
17 changes: 17 additions & 0 deletions src/main/java/ca/levimiller/smsbridge/service/FileService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package ca.levimiller.smsbridge.service;

import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;

public interface FileService {

/**
* Gets the file at the given url.
* @param url - file to fetch
* @return - file stream
* @throws URISyntaxException - if url is invalid
* @throws IOException - if an io exception occurs
*/
InputStream getFileStream(String url) throws URISyntaxException, IOException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package ca.levimiller.smsbridge.service;

import ca.levimiller.smsbridge.data.model.ChatUser;
import ca.levimiller.smsbridge.data.model.Media;

public interface MatrixAttachmentService {

/**
* Sends the given attachment to matrix.
* @param fromUser - user sending the attachment
* @param attachment - attachment to send
*/
void sendAttachment(ChatUser fromUser, String roomId, Media attachment);

/**
* Returns true if the given content type is supported by the attachment service.
* @param contentType - content type to check
* @return - true if supported
*/
boolean supportsType(String contentType);
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package ca.levimiller.smsbridge.service;

import io.github.ma1uta.matrix.event.Event;
import io.github.ma1uta.matrix.event.content.EventContent;

public interface MatrixEventService<T extends Event<?>> {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package ca.levimiller.smsbridge.service;

import ca.levimiller.smsbridge.data.model.Media;

public interface OutgoingAttachmentService {

/**
* Sends the given attachment to matrix.
* @param attachment - attachment to send
*/
void sendAttachment(Media attachment);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package ca.levimiller.smsbridge.service.impl;

import ca.levimiller.smsbridge.service.FileService;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.net.URL;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.client.LaxRedirectStrategy;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;

@Service
@Primary
@Qualifier("simpleFileService")
public class FileServiceImpl implements FileService {

@Override
public InputStream getFileStream(String url) throws URISyntaxException, IOException {
// Download file
CloseableHttpClient httpclient = HttpClients.custom()
.setRedirectStrategy(new LaxRedirectStrategy())
.build();
HttpGet get = new HttpGet(new URL(url).toURI());
HttpResponse response = httpclient.execute(get);
return response.getEntity().getContent();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package ca.levimiller.smsbridge.service.impl;

import org.springframework.stereotype.Service;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;

@Service
public class HostedUrlService {

/**
* Gets the hosted url from the current context path.
* @return - base url of app.
*/
public String getBaseUrl() {
return ServletUriComponentsBuilder.fromCurrentContextPath().build().toUriString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
import ca.levimiller.smsbridge.data.model.ChatUser;
import ca.levimiller.smsbridge.data.model.Message;
import ca.levimiller.smsbridge.error.NotFoundException;
import ca.levimiller.smsbridge.service.AttachmentService;
import ca.levimiller.smsbridge.service.ChatService;
import ca.levimiller.smsbridge.service.MatrixAttachmentService;
import ca.levimiller.smsbridge.service.RoomService;
import ca.levimiller.smsbridge.service.UserService;
import io.github.ma1uta.matrix.client.AppServiceClient;
Expand All @@ -24,14 +24,14 @@ public class MatrixChatService implements ChatService {
private final RoomService roomService;
private final UserService userService;
private final AppServiceClient matrixClient;
private final AttachmentService attachmentService;
private final MatrixAttachmentService attachmentService;

@Inject
public MatrixChatService(
ChatUserRepository chatUserRepository,
RoomService roomService, UserService userService,
AppServiceClient matrixClient,
AttachmentService attachmentService) {
MatrixAttachmentService attachmentService) {
this.chatUserRepository = chatUserRepository;
this.roomService = roomService;
this.userService = userService;
Expand Down
Loading

0 comments on commit 908bf77

Please sign in to comment.