Рубрики
Без рубрики

Расписание поста на Reddit с весны

Как создать простое приложение Spring для расписания, редактирования и удаления сообщений с помощью API Reddit.

Автор оригинала: baeldung.

1. Обзор

В более ранних частях этого примера мы создали простое приложение и процесс проверки подлинности OAuth с помощью API Reddit.

Давайте сейчас построим что-то полезное с Reddit – поддержку планирование Сообщений для последнего.

2. Пользователь и почта

Во-первых, давайте создадим два основных сущности – Пользователь и Почтовые . Пользователь будет отслеживать имя пользователя плюс некоторые дополнительные Oauth информация:

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

    @Column(nullable = false)
    private String username;

    private String accessToken;
    private String refreshToken;
    private Date tokenExpiration;

    private boolean needCaptcha;

    // standard setters and getters
}

Далее – Почтовые лицо – проведение информации, необходимой для отправки ссылки на Reddit: титульный , URL , subreddit , … и так далее.

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

    @Column(nullable = false) private String title;
    @Column(nullable = false) private String subreddit;
    @Column(nullable = false) private String url;
    private boolean sendReplies;

    @Column(nullable = false) private Date submissionDate;

    private boolean isSent;

    private String submissionResponse;

    @ManyToOne
    @JoinColumn(name = "user_id", nullable = false)
    private User user;
    // standard setters and getters
}

3. Слой настойчивости

Мы будем использовать Весенние данные JPA для сохранения , так что не так много, чтобы посмотреть здесь, кроме известных определений интерфейса для наших хранилищ:

  • ПользовательРепозиторий:
public interface UserRepository extends JpaRepository {

    User findByUsername(String username);

    User findByAccessToken(String token);
}
  • Пострепозиторий:
public interface PostRepository extends JpaRepository {

    List findBySubmissionDateBeforeAndIsSent(Date date, boolean isSent);

    List findByUser(User user);
}

4. Планировщик

Для аспектов планирования приложения, мы также собираемся сделать хорошее использование вне коробки Поддержка Весна.

Мы определяем задачу для запуска каждую минуту; это будет просто искать Сообщения, которые должны быть представлены на Reddit:

public class ScheduledTasks {
    private final Logger logger = LoggerFactory.getLogger(getClass());
    
    private OAuth2RestTemplate redditRestTemplate;
    
    @Autowired
    private PostRepository postReopsitory;

    @Scheduled(fixedRate = 1 * 60 * 1000)
    public void reportCurrentTime() {
        List posts = 
          postReopsitory.findBySubmissionDateBeforeAndIsSent(new Date(), false);
        for (Post post : posts) {
            submitPost(post);
        }
    }

    private void submitPost(Post post) {
        try {
            User user = post.getUser();
            DefaultOAuth2AccessToken token = 
              new DefaultOAuth2AccessToken(user.getAccessToken());
            token.setRefreshToken(new DefaultOAuth2RefreshToken((user.getRefreshToken())));
            token.setExpiration(user.getTokenExpiration());
            redditRestTemplate.getOAuth2ClientContext().setAccessToken(token);
            
            UsernamePasswordAuthenticationToken userAuthToken = 
              new UsernamePasswordAuthenticationToken(
              user.getUsername(), token.getValue(), 
              Arrays.asList(new SimpleGrantedAuthority("ROLE_USER")));
            SecurityContextHolder.getContext().setAuthentication(userAuthToken);

            MultiValueMap param = new LinkedMultiValueMap();
            param.add("api_type", "json");
            param.add("kind", "link");
            param.add("resubmit", "true");
            param.add("then", "comments");
            param.add("title", post.getTitle());
            param.add("sr", post.getSubreddit());
            param.add("url", post.getUrl());
            if (post.isSendReplies()) {
                param.add(RedditApiConstants.SENDREPLIES, "true");
            }

            JsonNode node = redditRestTemplate.postForObject(
              "https://oauth.reddit.com/api/submit", param, JsonNode.class);
            JsonNode errorNode = node.get("json").get("errors").get(0);
            if (errorNode == null) {
                post.setSent(true);
                post.setSubmissionResponse("Successfully sent");
                postReopsitory.save(post);
            } else {
                post.setSubmissionResponse(errorNode.toString());
                postReopsitory.save(post);
            }
        } catch (Exception e) {
            logger.error("Error occurred", e);
        }
    }
}

Обратите внимание, что в случае, если что-то пойдет не так, Сообщение не будет помечено как отправлено – так следующий цикл будет пытаться представить его снова через одну минуту .

5. Процесс входа

С новой сущностью пользователя, держащей некоторую конкретную информацию о безопасности, мы должны изменить наш простой процесс входа, чтобы хранить эту информацию :

@RequestMapping("/login")
public String redditLogin() {
    JsonNode node = redditRestTemplate.getForObject(
      "https://oauth.reddit.com/api/v1/me", JsonNode.class);
    loadAuthentication(node.get("name").asText(), redditRestTemplate.getAccessToken());
    return "redirect:home.html";
}

И нагрузкаАкенция () :

private void loadAuthentication(String name, OAuth2AccessToken token) {
    User user = userReopsitory.findByUsername(name);
    if (user == null) {
        user = new User();
        user.setUsername(name);
    }

    if (needsCaptcha().equalsIgnoreCase("true")) {
        user.setNeedCaptcha(true);
    } else {
        user.setNeedCaptcha(false);
    }

    user.setAccessToken(token.getValue());
    user.setRefreshToken(token.getRefreshToken().getValue());
    user.setTokenExpiration(token.getExpiration());
    userReopsitory.save(user);

    UsernamePasswordAuthenticationToken auth = 
      new UsernamePasswordAuthenticationToken(user, token.getValue(), 
      Arrays.asList(new SimpleGrantedAuthority("ROLE_USER")));
    SecurityContextHolder.getContext().setAuthentication(auth);
}

Обратите внимание, как пользователь автоматически создается, если он еще не существует. Это делает процесс “Войти с Reddit” создать локального пользователя в системе на первом входе.

6. Страница расписания

Далее – давайте посмотрим на страницу, которая позволяет планировать новые сообщения:

@RequestMapping("/postSchedule")
public String showSchedulePostForm(Model model) {
    boolean isCaptchaNeeded = getCurrentUser().isCaptchaNeeded();
    if (isCaptchaNeeded) {
        model.addAttribute("msg", "Sorry, You do not have enought karma");
        return "submissionResponse";
    }
    return "schedulePostForm";
}
private User getCurrentUser() {
    return (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
}

расписаниеПостФорма.html :

Обратите внимание, как мы должны проверить Captcha. Это потому, что – если пользователь имеет менее 10 карма- – они не могут запланировать должность без заполнения Captcha.

7. POSTing

При отправлении формы Расписание информация о должности просто проверяется и сохраняется быть подобраны планировщик позже:

@RequestMapping(value = "/api/scheduledPosts", method = RequestMethod.POST)
@ResponseBody
public Post schedule(@RequestBody Post post) {
    if (submissionDate.before(new Date())) {
        throw new InvalidDateException("Scheduling Date already passed");
    }

    post.setUser(getCurrentUser());
    post.setSubmissionResponse("Not sent yet");
    return postReopsitory.save(post);
}

8. Список запланированных сообщений

Давайте теперь реализуем простой API REST для получения запланированных сообщений у нас есть:

@RequestMapping(value = "/api/scheduledPosts")
@ResponseBody
public List getScheduledPosts() {
    User user = getCurrentUser();
    return postReopsitory.findByUser(user);
}

И простой, быстрый способ отображать эти запланированные сообщения на переднем конце :

Post titleSubmission Date

9. Редактировать запланированный пост

Далее – давайте посмотрим, как мы можем редактировать запланированный пост.

Начнем с переднего конца – во-первых, очень простой операции MVC:

@RequestMapping(value = "/editPost/{id}", method = RequestMethod.GET)
public String showEditPostForm() {
    return "editPostForm";
}

После простого API, вот передний конец потребления его:

Теперь давайте посмотрим на ОТДЫХ API :

@RequestMapping(value = "/api/scheduledPosts/{id}", method = RequestMethod.GET) 
@ResponseBody 
public Post getPost(@PathVariable("id") Long id) { 
    return postReopsitory.findOne(id); 
}

@RequestMapping(value = "/api/scheduledPosts/{id}", method = RequestMethod.PUT) 
@ResponseStatus(HttpStatus.OK) 
public void updatePost(@RequestBody Post post, @PathVariable Long id) { 
    if (post.getSubmissionDate().before(new Date())) { 
        throw new InvalidDateException("Scheduling Date already passed"); 
    } 
    post.setUser(getCurrentUser()); 
    postReopsitory.save(post); 
}

10. Внеплановые/Удалить сообщение

Мы также предоставим простую операцию удаления для любого из запланированных сообщений:

@RequestMapping(value = "/api/scheduledPosts/{id}", method = RequestMethod.DELETE)
@ResponseStatus(HttpStatus.OK)
public void deletePost(@PathVariable("id") Long id) {
    postReopsitory.delete(id);
}

Вот как мы называем это со стороны клиента:

Delete

11. Заключение

В этой части нашего кейс-исследования Reddit мы создали первый нетривиальный бит функциональности с помощью API Reddit – планирования сообщений.

Это супер-полезная функция для серьезного пользователя Reddit, особенно с учетом как чувствительные к времени материалы Reddit .

Далее – мы будем строить еще более полезную функциональность, чтобы помочь с получением содержания upvoted на Reddit – машинного обучения предложения.

полная реализация этого учебника можно найти в проект github – это проект на основе Eclipse, поэтому он должен быть легким для импорта и запуска, как она есть.