Machen Sie es einfach, Reddit einzuplanen

Vereinfachen Sie die Planung von Reddit

1. Überblick

In diesem Artikel gehen wir zucontinue the case study undadd a new feature to the Reddit application mit dem Ziel, das Planen von Artikeln zu vereinfachen.

Anstatt langsam jeden Artikel von Hand in die Zeitplan-Benutzeroberfläche einzufügen, kann der Benutzer jetzt einfach einige Lieblingsseiten zum Posten von Artikeln in Reddit verwenden. Dafür verwenden wir RSS.

2. DieSite-Entität

Zunächst erstellen wir eine Entität zur Darstellung der 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;
}

Beachten Sie, dass das Feldurl die URL vonthe RSS feed of the site darstellt.

3. Das Repository und der Service

Weiter - Lässt das Repository für die Arbeit mit der neuen Site-Entität erstellen:

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

Und der 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. Laden Sie Daten aus dem Feed

Lassen Sie uns nun sehen, wie Sie die Artikeldetails aus dem Website-Feed mithilfe der Rom-Bibliothek laden.

Wir müssen zuerst Rom zu unserenpom.xmlhinzufügen:


    com.rometools
    rome
    1.5.0

Und verwenden Sie es dann, um die Feeds der Websites zu analysieren:

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;
}

Schließlich - hier ist das einfache DTO, das wir in der Antwort verwenden werden:

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

5. Ausnahmebehandlung

Beachten Sie, dass wir beim Parsen des Feeds die gesamte Parsing-Logik in einentry-catch-Block einschließen und - im Falle einer Ausnahme (einer Ausnahme) - in einen Block einschließen und ihn auslösen.

Der Grund dafür ist einfach -we need to control the type of exception that gets thrown out of the parsing process -, damit wir diese Ausnahme behandeln und dem Client der API eine angemessene Antwort geben können:

@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. Die Seite Seite

6.1. Zeigen Sie die Sites an

Zuerst sehen wir, wie die Liste der Sites des angemeldeten Benutzers angezeigt wird:

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

Und hier ist das sehr einfache Vorderteil:

Site NameFeed URLActions

6.2. Neue Site hinzufügen

Lassen Sie uns als Nächstes sehen, wie ein Benutzer eine neue Lieblingssite erstellen kann:

@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);
}

Und hier ist die - ebenfalls sehr einfache - Client-Seite:

6.3. Einen Feed validieren

Die Validierung eines neuen Feeds ist etwas kostspielig - wir müssen den Feed tatsächlich abrufen und auswerten, um ihn vollständig zu validieren. Hier ist die einfache Servicemethode:

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

6.3. Löschen Sie eine Site

Nun wollen wir sehen, wie der Benutzerdelete a site from their list of favorite sites kann:

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

Und hier die - wieder sehr einfache - Service Level Methode:

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

7. Planen Sie einen Beitrag von einer Site aus

Beginnen wir jetzt mit der Verwendung dieser Websites und implementieren eine grundlegende Methode, mit der ein Benutzer einen neuen Beitrag so planen kann, dass er nicht manuell zu Reddit wechselt, sondern indem ein Artikel von einer vorhandenen Website geladen wird.

7.1. Planungsformular ändern

Beginnen wir mit der Client-Site und ändern die vorhandenenschedulePostForm.html - wir werden hinzufügen:


      Beachten Sie, dass wir Folgendes hinzugefügt haben:

      • Klicken Sie auf die Schaltfläche „Load from my Sites“, um den Vorgang zu starten

      • das Pop-up - zeigt die Liste der Websites und deren Artikel

      7.2. Laden Sie die Sites

      Das Laden der Seiten im Popup ist mit etwas Javascript relativ einfach:

      $('#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. Integrationstests

      Zum Schluss testen wir unsereSiteService auf zwei verschiedenen Feedformaten:

      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());
              }
          }
      }

      Hier gibt es eindeutig einige Überschneidungen, aber wir können uns später darum kümmern.

      9. Fazit

      In dieser Folge haben wir uns auf eine neue, kleine Funktion konzentriert, die das Planen des Posts an Reddit vereinfacht.