I have a MarkdownService which does the following:

  1. Opens a transaction

  2. Fetches the Markdown entity from the repository

 Markdown markdown = markdownRepository
      .findById(markdownId)
      .orElseThrow(EntityNotFoundException::new);
  1. Retrieves the original filename and together with the markdownId, creates a new filename according to the business rules.
String originalFilename = storageService.getOriginalFilename(file);

String filename = Markdown.normalizeMdPath(markdownId + "-" + originalFilename);
  1. Saves the file to disk at a certain path, /articles/markdowns
 storageService.store(
      file,
      this.uploadMarkdownsPath,
      filename
    );
  1. Updates the entity with the filename only, not the full path
    markdown.setMdPath(filename);
  1. Maps the Markdown entity to it's corresponding MarkdownDto using mapstruct
    MarkdownDto dto = MarkdownMapper.INSTANCE.toMarkdownDto(markdown);
  1. Now, because the application is running locally, gets the full path of the uploaded Markdown file and updates the DTO, returning it to the controller.
 String fullPath = ServletUriComponentsBuilder
      .fromCurrentContextPath()
      .path("/markdown/download/")
      .path(dto.getMdPath())
      .toUriString();

    dto.setMdPath(fullPath);

    return dto;

Now, the application will also run in production in AWS Fargate, under a certain domain name, and the actual markdowns will be stored in the cloud, not even in the file system.

My question is, can I somehow modify the Mapstruct mapper to set the full path of the markdown file at the DTO level, depending on a Spring environment variable? I would avoid doing this manual mapping on every controller method which returns a markdown DTO.

@Mapper
public interface MarkdownMapper {
  MarkdownMapper INSTANCE = Mappers.getMapper(MarkdownMapper.class);

  MarkdownDto toMarkdownDto(Markdown markdown);
}

Is there a better way to handle this? Maybe a totally different solution?

Here is the full code:

  @Transactional
  public MarkdownDto uploadMarkdownFile(Long markdownId, MultipartFile file) throws FileNotFoundException {
    Markdown markdown = markdownRepository
      .findById(markdownId)
      .orElseThrow(EntityNotFoundException::new);

    String originalFilename = storageService.getOriginalFilename(file);

    String filename = Markdown.normalizeMdPath(markdownId + "-" + originalFilename);

    storageService.store(
      file,
      this.uploadMarkdownsPath,
      filename
    );

    markdown.setMdPath(filename);

    MarkdownDto dto = MarkdownMapper.INSTANCE.toMarkdownDto(markdown);

    String fullPath = ServletUriComponentsBuilder
      .fromCurrentContextPath()
      .path("/markdown/download/")
      .path(dto.getMdPath())
      .toUriString();

    dto.setMdPath(fullPath);

    return dto;
  }

Thank you!

1

There are 1 best solutions below

0
On

You could use inject a context into the mapper call and define an @AfterMapping.

@Mapper(componentModel = "spring", unmappedTargetPolicy = ReportingPolicy.ERROR)
public interface UserPOMapper {
    UserDTO toUserDTO(UserPO userPo);
    
    @Mapping(target = "id", ignore = true)
    UserPO fromUserDTO(UserDTO userDto);
    
    @AfterMapping
    default void after(@MappingTarget UserDTO.UserDTOBuilder u, @Context String url) {
        u.firstName(url);
    }
    
    UserDTO toUserDTOWithDetails(UserPO userPO, @Context String url);
}