New `bootstrap.json` endpoint for starting up Discourse

New bootstrap.json endpoint for starting up Discourse

Discourse needs a bunch of data preloaded before it can start up. Normally we throw blobs of this into the HTML document that is requested but in some cases that’s awkward to retrieve.

For example with Ember CLI you have a separate javascript application that needs to make its own HTML.

This API endpoint returns a JSON object with all the data Discourse needs to bootstrap and start up.

diff --git a/app/controllers/bootstrap_controller.rb b/app/controllers/bootstrap_controller.rb
new file mode 100644
index 0000000..dca7007
--- /dev/null
+++ b/app/controllers/bootstrap_controller.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+class BootstrapController < ApplicationController
+  include ApplicationHelper
+  include ActionView::Helpers::AssetUrlHelper
+
+  # This endpoint allows us to produce the data required to start up Discourse via JSON API,
+  # so that you don't have to scrape the HTML for `data-*` payloads
+  def index
+    locale = script_asset_path("locales/#{I18n.locale}")
+
+    preload_anonymous_data
+    if current_user
+      current_user.sync_notification_channel_position
+      preload_current_user_data
+    end
+
+    bootstrap = {
+      theme_ids: theme_ids,
+      title: SiteSetting.title,
+      current_homepage: current_homepage,
+      locale_script: "#{Discourse.base_url}#{locale}",
+      setup_data: client_side_setup_data,
+      preloaded: @preloaded
+    }
+
+    render_json_dump(bootstrap: bootstrap)
+  end
+end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 482b247..f6bda65 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -356,6 +356,7 @@ module ApplicationHelper
   end
 
   def loading_admin?
+    return false unless defined?(controller)
     controller.class.name.split("::").first == "Admin"
   end
 
diff --git a/config/routes.rb b/config/routes.rb
index 69ade71..e763252 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -14,6 +14,8 @@ Discourse::Application.routes.draw do
   match "/404", to: "exceptions#not_found", via: [:get, :post]
   get "/404-body" => "exceptions#not_found_body"
 
+  get "/bootstrap" => "bootstrap#index"
+
   post "webhooks/aws" => "webhooks#aws"
   post "webhooks/mailgun"  => "webhooks#mailgun"
   post "webhooks/mailjet"  => "webhooks#mailjet"
diff --git a/spec/requests/bootstrap_controller_spec.rb b/spec/requests/bootstrap_controller_spec.rb
new file mode 100644
index 0000000..6495eb4
--- /dev/null
+++ b/spec/requests/bootstrap_controller_spec.rb
@@ -0,0 +1,40 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe BootstrapController do
+
+  it "returns data as anonymous" do
+    get "/bootstrap.json"
+    expect(response.status).to eq(200)
+
+    json = response.parsed_body
+    expect(json).to be_present
+
+    bootstrap = json['bootstrap']
+    expect(bootstrap).to be_present
+    expect(bootstrap['title']).to be_present
+    expect(bootstrap['setup_data']['base_url']).to eq(Discourse.base_url)
+    preloaded = bootstrap['preloaded']
+    expect(preloaded['site']).to be_present
+    expect(preloaded['siteSettings']).to be_present
+    expect(preloaded['currentUser']).to be_blank
+    expect(preloaded['topicTrackingStates']).to be_blank
+  end
+
+  it "returns user data when authenticated" do
+    user = Fabricate(:user)
+    sign_in(user)
+    get "/bootstrap.json"
+    expect(response.status).to eq(200)
+
+    json = response.parsed_body
+    expect(json).to be_present
+
+    bootstrap = json['bootstrap']
+    preloaded = bootstrap['preloaded']
+    expect(preloaded['currentUser']).to be_present
+    expect(preloaded['topicTrackingStates']).to be_present
+  end
+
+end

GitHub sha: 22789e02

1 Like