Возобновление работы виджета с помощью AJAX-запроса

Эта тема уже поднималась несколько раз, но обсуждения заходили в тупик (обычно потому, что разработчик на самом деле не требует вызова AJAX). Однако я разрабатываю плагин, который обращается к данным, не хранящимся в базе данных Discourse (автономный бот Discord), и пытаюсь использовать эти данные для украшения постов.

export default Component.extend({
init() {
this._super(…arguments);

var self = this;
const store = getOwner(this).lookup("service:store");

  let positions = [];

store
  .findAll("position", {
    ...args,
  })
  .then((data) => {

    for (const [key, position] of Object.entries(data.content)) {
      positions.pushObject(EmberObject.create({ ...position }));
    }
  });

withPluginApi("1.4.0", (api) => {
  api.reopenWidget("post", {
    html(attrs) {
      let position = positions.filter(
        (position) => position.user.id == attrs.user.id
      );

      if (position.length > 0) {
        attrs.cooked = "Has Position";
      }

      return this.attach("post-article", attrs);
    },
  });
});

},
});

Я пробовал различные варианты этого кода, включая вызов функции API в части .then запроса к хранилищу и так далее, но все они приводят к одному и тому же результату: изменения в постах не отображаются, пока вы не прокрутите страницу.

Все эти попытки были сделаны поздно ночью, поэтому вполне вероятно, что я упускаю что-то совершенно очевидное. Однако любая помощь будет очень кстати.

Думаю, это зависит от того, что вы пытаетесь сделать, но почему бы не использовать официальную Ruby-библиотеку для Discord, чтобы получить данные в базу данных Discourse, сериализовать их для клиента и затем использовать в виджете?

Казалось, это излишнее усложнение, поскольку основного API приложений достаточно для большинства задач, но возможно, я неправильно понимаю реализацию, так как, признаюсь, мне ещё нужно немного изучить Ruby-часть Discourse.

Хотя, если мне не удастся настроить украшение постов, скорее всего, придётся пойти этим путём.

Возвращаясь к вопросу, чтобы выразить благодарность @merefield: я подробнее изучил сериализацию и смог решить задачу, выполнив вызов API непосредственно в сериализаторе, а не запрашивая данные из JS, как это делал раньше.

Уверен, что существует множество причин, по которым такой подход не рекомендуется, но он работает в моём случае, поэтому я использую его. Тем, кто найдёт это сообщение в будущем: вы можете добавить данные в сериализатор с помощью следующего кода:

add_to_serializer(:topic_view, :data_name) do
    JSON.parse(Net::HTTP.get(URI('https://yourwebsite.com?topic_id=' + object.topic.id.to_s)))
end

Это позволяет затем получить доступ к данным внутри вызова reopenWidget. В моём случае я добавляю данные, относящиеся к теме, в сериализатор topic_view, и получаю к ним доступ при модификации поста следующим образом:

api.reopenWidget("post", {
          html(attrs) {
            let data_name = this.model.topic.data_name;

            // Выполняем действия с данными

            return this.attach("post-article", attrs);
          },
        });

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

Тем, кто по каким-либо причинам хочет решить задачу именно в JS, соответствующий код, по-видимому, можно найти в плагине discourse-encrypt, где происходит расшифровка постов. Однако я обнаружил, что следующий метод реализовать гораздо проще: discourse-encrypt/assets/javascripts/discourse/initializers/decrypt-posts.js at 255724ebc5fc3956f26beca09c1f7cb273d76eb2 · discourse/discourse-encrypt · GitHub

Странно, но это, похоже, ломает компонент темы «Миниатюры тем», делая изображения гигантскими: GitHub - discourse/discourse-topic-thumbnails: Display thumbnails in topic lists · GitHub

Действительно странное взаимодействие, однако других заметных проблем нет.

Хм… Не уверен, что это архитектурно грамотно. Хотя здорово, что вы исследуете бэкенд-решение (удалённые вызовы сервера почти всегда должны происходить между процессами на бэкенде, особенно когда речь идёт об аутентификации и авторизации), данные, по мнению многих, должны загружаться асинхронно, а сериализатор не должен ждать завершения удалённого вызова для завершения своей работы. Как минимум, вы должны обернуть этот вызов в кэш?

Посмотрите, как это сработает, но обратите пристальное внимание на время загрузки страницы до и после этого изменения — вы можете внести значительную задержку.

На практике это добавляет от 10 до 50 мс на загрузку, если вообще добавляет. Соединение обладает сверхнизкой задержкой, так как оба сервера находятся в одном дата-центре. Более того, метод, который я использую для подгрузки данных в компоненты, загружает страницы даже немного быстрее, чем Discourse рендерит свои собственные статические страницы (разница составляет около 100 мс). При этом учитывается операция с БД, которая использует идентификаторы Discord для добавления данных пользователей в ответы API перед их отправкой клиенту. (Ruby запрашивает данные у внешнего приложения → парсит JSON → выполняет цикл запросов к БД для добавления данных в ответ → генерирует JSON и возвращает его фронтенд-клиенту). Это понятно, учитывая огромный размер кодовой базы, но определенно существует внутренняя накладная нагрузка, которая позволяет таким запросам, традиционно считающимся «дорогостоящими», оставаться совершенно незаметными. При этом мы говорим о скорости света и звука, что для конечного пользователя всё равно выглядит невероятно быстро, так как Discourse — это потрясающая платформа.

Кэширование стало бы отличным дополнением, и я планирую изучить эту возможность. Я новичок в Ruby, поэтому буду обновлять информацию по мере изучения доступных функций. Я буду дополнять этот пост по мере продвижения, потому что считаю, что какой-то метод для этого, пусть даже на данный момент и «костыльный», может оказаться потенциально очень полезным для разработки плагинов. Для пользователей с существующими приложениями возможность использовать знакомый API для эффективной подгрузки необходимых данных в нужные места без необходимости создавать системы синхронизации данных между своей базой данных и Discourse или возиться с дополнительными моделями и миграциями БД, значительно сокращает время разработки и позволяет авторам плагинов работать в одних из наиболее хорошо документированных областей приложения (например, в API плагинов).

Я очень ценю ваши insights, безусловно, с нетерпением жду возможности узнать больше об этой платформе и, надеюсь, внести свой вклад в будущем.