diff --git a/app/controllers/async_renderer_controller.rb b/app/controllers/async_renderer_controller.rb new file mode 100644 index 00000000..49caa567 --- /dev/null +++ b/app/controllers/async_renderer_controller.rb @@ -0,0 +1,11 @@ +class AsyncRendererController < ApplicationController + def async_render + renderer = "Async::#{params[:view_model]}ViewModel".constantize + view_model = renderer.new(current_user, params) + html = view_model.async_render + rescue => e + html = view_model.render_error + ensure + render inline: html.html_safe + end +end diff --git a/app/javascript/controllers/async_renderer_controller.js b/app/javascript/controllers/async_renderer_controller.js new file mode 100644 index 00000000..7deedc9d --- /dev/null +++ b/app/javascript/controllers/async_renderer_controller.js @@ -0,0 +1,26 @@ +import { Controller } from "@hotwired/stimulus" + +export default class extends Controller { + static targets = ["frame"] + static values = { + viewModel: String, + params: String + } + + connect() { + this.render(); + } + + async render() { + const params = JSON.parse(this.paramsValue) + const queryString = new URLSearchParams(params).toString(); + const response = await fetch(`/async_render?view_model=${this.viewModelValue}&${queryString}`); + + if (response.ok) { + const html = await response.text(); + this.frameTarget.innerHTML = html; + } else { + this.frameTarget.innerHTML = `
Failed to load
`; + } + } +} diff --git a/app/services/k8/client.rb b/app/services/k8/client.rb index 84bb197c..62ae7130 100644 --- a/app/services/k8/client.rb +++ b/app/services/k8/client.rb @@ -16,6 +16,10 @@ module K8 new(project.cluster.kubeconfig) end + def self.from_cluster(cluster) + new(cluster.kubeconfig) + end + def initialize(kubeconfig) @_kubeconfig = kubeconfig @kubeconfig = kubeconfig.is_a?(String) ? JSON.parse(kubeconfig) : kubeconfig @@ -51,6 +55,11 @@ module K8 cluster_info["server"] end + def version + result = K8::Kubectl.new(@_kubeconfig).call("version -o yaml") + YAML.safe_load(result) + end + def kubecontext @kubeconfig["current-context"] end diff --git a/app/view_models/async/base_view_model.rb b/app/view_models/async/base_view_model.rb new file mode 100644 index 00000000..9c6c8529 --- /dev/null +++ b/app/view_models/async/base_view_model.rb @@ -0,0 +1,34 @@ +class Async::BaseViewModel + attr_accessor :current_user, :params + + def initialize(current_user, params) + @current_user = current_user + @params = params + validate! + end + + def name + self.class.name.sub(/^Async::/, '').sub(/ViewModel$/, '') + end + + def self.expects(*args) + @expected_params ||= [] + @expected_params.concat(args) + end + + def self.expected_params + @expected_params || [] + end + + def render_error + "
Error
" + end + + def validate! + self.class.expected_params.each do |param| + unless @params.key?(param) + raise ArgumentError, "Missing expected parameter: #{param}" + end + end + end +end diff --git a/app/view_models/async/k8/configuration_view_model.rb b/app/view_models/async/k8/configuration_view_model.rb new file mode 100644 index 00000000..851ed8e1 --- /dev/null +++ b/app/view_models/async/k8/configuration_view_model.rb @@ -0,0 +1,16 @@ +class Async::K8::ConfigurationViewModel < Async::BaseViewModel + expects :cluster_id + + def version + cluster = current_user.clusters.find(params[:cluster_id]) + version ||= K8::Client.from_cluster(cluster).version['serverVersion']['gitVersion'] + end + + def initial_render + "
" + end + + def async_render + "
#{version}
" + end +end diff --git a/app/views/clusters/show.html.erb b/app/views/clusters/show.html.erb index 8516458a..5c3c60cb 100644 --- a/app/views/clusters/show.html.erb +++ b/app/views/clusters/show.html.erb @@ -1,7 +1,6 @@ <%= content_for :title, "Clusters ##{@cluster.id}" %> <%= turbo_stream_from @cluster %> - <%= cluster_layout(@cluster) do %>
@@ -17,6 +16,19 @@ <% end %>
+
+
Configuration
+
+ <%= render( + "shared/partials/async_renderer", + view_model: Async::K8::ConfigurationViewModel.new( + current_user, + cluster_id: @cluster.id + ) + ) %> +
+
+ <% if @cluster.projects.any? %>

Projects


diff --git a/app/views/shared/partials/_async_renderer.html.erb b/app/views/shared/partials/_async_renderer.html.erb new file mode 100644 index 00000000..ed708b32 --- /dev/null +++ b/app/views/shared/partials/_async_renderer.html.erb @@ -0,0 +1,10 @@ +
+
+ <%= view_model.initial_render.html_safe %> +
+
+ diff --git a/config/routes.rb b/config/routes.rb index 08a0a69b..3a07f665 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -95,6 +95,7 @@ Rails.application.routes.draw do # Render dynamic PWA files from app/views/pwa/* get "service-worker" => "rails/pwa#service_worker", as: :pwa_service_worker get "manifest" => "rails/pwa#manifest", as: :pwa_manifest + get "async_render" => "async_renderer#async_render" # Public marketing homepage if Rails.application.config.local_mode