Rendez-le facile pour programmer à Reddit

Rendez-le facile à programmer pour Reddit

1. Vue d'ensemble

Dans cet article, nous allons passer àcontinue the case study etadd a new feature to the Reddit application, dans le but de simplifier considérablement la planification des articles.

Au lieu d’ajouter lentement chaque article à la main dans l’interface utilisateur de planification, l’utilisateur peut désormais n’avoir que quelques sites favoris pour publier des articles sur Reddit. Nous allons utiliser RSS pour ce faire.

2. L'entitéSite

Tout d'abord, créons une entité pour représenter le site:

@Entity
public class Site {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    @Column(nullable = false)
    private String name;

    @Column(nullable = false)
    private String url;

    @ManyToOne
    @JoinColumn(name = "user_id", nullable = false)
    private User user;
}

Notez que le champurl représente l'URL dethe RSS feed of the site.

3. Le référentiel et le service

Suivant - Permet de créer le référentiel à utiliser avec la nouvelle entité de site:

public interface SiteRepository extends JpaRepository {
    List findByUser(User user);
}

Et le service:

public interface ISiteService {

    List getSitesByUser(User user);

    void saveSite(Site site);

    Site findSiteById(Long siteId);

    void deleteSiteById(Long siteId);
}
@Service
public class SiteService implements ISiteService {

    @Autowired
    private SiteRepository repo;

    @Override
    public List getSitesByUser(User user) {
        return repo.findByUser(user);
    }

    @Override
    public void saveSite(Site site) {
        repo.save(site);
    }

    @Override
    public Site findSiteById(Long siteId) {
        return repo.findOne(siteId);
    }

    @Override
    public void deleteSiteById(Long siteId) {
        repo.delete(siteId);
    }
}

4. Charger des données à partir du flux

Voyons maintenant comment charger les détails des articles à partir du flux du site Web à l'aide de la bibliothèque de Rome.

Nous devons d'abord ajouter Rome dans nospom.xml:


    com.rometools
    rome
    1.5.0

Et ensuite, utilisez-le pour analyser les flux des sites:

public List getArticlesFromSite(Long siteId) {
    Site site = repo.findOne(siteId);
    return getArticlesFromSite(site);
}

List getArticlesFromSite(Site site) {
    List entries;
    try {
        entries = getFeedEntries(site.getUrl());
    } catch (Exception e) {
        throw new FeedServerException("Error Occurred while parsing feed", e);
    }
    return parseFeed(entries);
}

private List getFeedEntries(String feedUrl)
  throws IllegalArgumentException, FeedException, IOException {
    URL url = new URL(feedUrl);
    SyndFeed feed = new SyndFeedInput().build(new XmlReader(url));
    return feed.getEntries();
}

private List parseFeed(List entries) {
    List articles = new ArrayList();
    for (SyndEntry entry : entries) {
        articles.add(new SiteArticle(
          entry.getTitle(), entry.getLink(), entry.getPublishedDate()));
    }
    return articles;
}

Enfin, voici le DTO simple que nous allons utiliser dans la réponse:

public class SiteArticle {
    private String title;
    private String link;
    private Date publishDate;
}

5. Gestion des exceptions

Remarquez comment, lors de l'analyse du flux, nous enveloppons l'ensemble de la logique d'analyse dans un bloctry-catch et, en cas d'exception (toute exception), nous l'enveloppons et le lançons.

La raison en est simple -we need to control the type of exception that gets thrown out of the parsing process - afin que nous puissions ensuite gérer cette exception et fournir une réponse appropriée au client de l'API:

@ExceptionHandler({ FeedServerException.class })
public ResponseEntity handleFeed(RuntimeException ex, WebRequest request) {
    logger.error("500 Status Code", ex);
    String bodyOfResponse = ex.getLocalizedMessage();
    return new ResponseEntity(bodyOfResponse, new HttpHeaders(),
      HttpStatus.INTERNAL_SERVER_ERROR);
}




6. La page des sites

6.1. Afficher les sites

Tout d'abord, nous verrons comment afficher la liste des sites appartenant à l'utilisateur connecté:

@RequestMapping(value = "/sites")
@ResponseBody
public List getSitesList() {
    return service.getSitesByUser(getCurrentUser());
}

Et voici l’avant très simple:

Site NameFeed URLActions

6.2. Ajouter un nouveau site

Voyons ensuite comment un utilisateur peut créer un nouveau site favori:

@RequestMapping(value = "/sites", method = RequestMethod.POST)
@ResponseStatus(HttpStatus.OK)
public void addSite(Site site) {
    if (!service.isValidFeedUrl(site.getUrl())) {
        throw new FeedServerException("Invalid Feed Url");
    }
    site.setUser(getCurrentUser());
    service.saveSite(site);
}

Et voici le - encore une fois très simple - côté client:

6.3. Valider un flux

La validation d'un nouveau flux est une opération assez coûteuse - nous devons récupérer le flux et l'analyser pour le valider complètement. Voici la méthode de service simple:

public boolean isValidFeedUrl(String feedUrl) {
    try {
        return getFeedEntries(feedUrl).size() > 0;
    } catch (Exception e) {
        return false;
    }
}

6.3. Supprimer un site

Voyons maintenant comment l’utilisateur peutdelete a site from their list of favorite sites:

@RequestMapping(value = "/sites/{id}", method = RequestMethod.DELETE)
@ResponseStatus(HttpStatus.OK)
public void deleteSite(@PathVariable("id") Long id) {
    service.deleteSiteById(id);
}

Et voici la méthode du niveau de service - encore une fois très simple -:

public void deleteSiteById(Long siteId) {
    repo.delete(siteId);
}

7. Planifier une publication à partir d'un site

Maintenant, commençons à utiliser ces sites et mettons en œuvre un moyen de base permettant à un utilisateur de planifier un nouveau message pour qu'il soit envoyé sur Reddit non pas manuellement, mais en chargeant un article à partir d'un site existant.

7.1. Modifier le formulaire de planification

Commençons par le site client et modifions lesschedulePostForm.html existants - nous allons ajouter:


      Notez que nous avons ajouté:

      • le bouton - «Load from my Sites» - pour démarrer le processus

      • la pop-up - montrant la liste des sites et leurs articles

      7.2. Charger les sites

      Charger les sites dans le popup est relativement facile avec un peu de javascript:

      $('#myModal').on('shown.bs.modal', function () {
          if($("#siteList").children().length > 0)
              return;
          $.get("sites", function(data){
              $.each(data, function( index, site ) {
                  $("#siteList").append('
    • '+site.name+'
    • ') }); }); });

      8. Tests d'intégration

      Enfin, testons nosSiteService sur deux formats de flux différents:

      public class SiteIntegrationTest {
      
          private ISiteService service;
      
          @Before
          public void init() {
              service = new SiteService();
          }
      
          @Test
          public void whenUsingServiceToReadWordpressFeed_thenCorrect() {
              Site site = new Site("/feed/");
              List articles = service.getArticlesFromSite(site);
      
              assertNotNull(articles);
              for (SiteArticle article : articles) {
                  assertNotNull(article.getTitle());
                  assertNotNull(article.getLink());
              }
          }
      
          @Test
          public void whenUsingRomeToReadBloggerFeed_thenCorrect() {
              Site site = new Site("http://blogname.blogspot.com/feeds/posts/default");
              List articles = service.getArticlesFromSite(site);
      
              assertNotNull(articles);
              for (SiteArticle article : articles) {
                  assertNotNull(article.getTitle());
                  assertNotNull(article.getLink());
              }
          }
      }

      Il y a clairement un peu de duplication ici, mais nous pourrons nous en occuper plus tard.

      9. Conclusion

      Dans cet article, nous nous sommes concentrés sur une nouvelle petite fonctionnalité - simplifiant la planification du message sur Reddit.