From c3ed2eb2cb6dd953da8d913d1e2f343f6b91170e Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Fri, 11 Aug 2023 17:30:54 -0600 Subject: [PATCH 01/61] Working on switching from a shared key to a public / private key pair --- lib/atomic_admin/jwt_token.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/atomic_admin/jwt_token.rb b/lib/atomic_admin/jwt_token.rb index 2762684..f78c5ae 100644 --- a/lib/atomic_admin/jwt_token.rb +++ b/lib/atomic_admin/jwt_token.rb @@ -76,6 +76,12 @@ def encoded_token(req) token end - + + def load_admin_jwks(options) + jwks_raw = Net::HTTP.get URI("https://localhost:3000/jwks.json") # TODO: make this configurable + jwks_keys = JSON.parse(jwks_raw) + + JWT::JWK::Set.new(jwks_keys).select { |k| k[:use] == "sig" } + end end end From a9b215263e2e3fc088158405d154d8f625a7be9d Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Fri, 25 Aug 2023 17:14:51 -0600 Subject: [PATCH 02/61] Got JWKs from the admin app working --- .../atomic_admin/application_controller.rb | 4 +- lib/atomic_admin.rb | 3 +- lib/atomic_admin/jwt_token.rb | 60 ++++++++++--------- 3 files changed, 37 insertions(+), 30 deletions(-) diff --git a/app/controllers/atomic_admin/application_controller.rb b/app/controllers/atomic_admin/application_controller.rb index c3a4813..3bc7634 100644 --- a/app/controllers/atomic_admin/application_controller.rb +++ b/app/controllers/atomic_admin/application_controller.rb @@ -1,8 +1,8 @@ module AtomicAdmin - class ApplicationController < ActionController::API + class ApplicationController < ActionController::API include AtomicAdmin::JwtToken # before_action :authenticate_user! # Use validate_token instead for now - before_action :validate_admin_app_token + before_action :validate_admin_app_token before_action :validate_token before_action :only_admins! diff --git a/lib/atomic_admin.rb b/lib/atomic_admin.rb index c08e8ac..5e94127 100644 --- a/lib/atomic_admin.rb +++ b/lib/atomic_admin.rb @@ -3,5 +3,6 @@ require "atomic_admin/jwt_token" module AtomicAdmin - # Your code goes here... + mattr_accessor :admin_jwks_url + mattr_accessor :audience end diff --git a/lib/atomic_admin/jwt_token.rb b/lib/atomic_admin/jwt_token.rb index f78c5ae..220fd6e 100644 --- a/lib/atomic_admin/jwt_token.rb +++ b/lib/atomic_admin/jwt_token.rb @@ -1,87 +1,93 @@ -## Note: This code is basically copied out of the starter app to authenticate +## Note: This code is basically copied out of the starter app to authenticate ## admin app api calls. Lives at /app/controllers/concerns/jwt_token.rb in ## starter app module AtomicAdmin module JwtToken - - ALGORITHM = "HS512".freeze + + ALGORITHM = "RS256".freeze class InvalidTokenError < StandardError; end - def self.valid?(token, secret = nil, algorithm = ALGORITHM) + def self.valid?(token, algorithm = ALGORITHM) decode(token, secret, true, algorithm) end - def self.decode(token, secret = nil, validate = true, algorithm = ALGORITHM) + def self.decode(token, validate = true, algorithm = ALGORITHM) + + load_admin_jwks = ->(options) do + Rails.cache.delete("atomic_admin_jwks") if options[:kid_not_found] + + # NOTE: the cached keys only expire when we recieve a kid_not_found error + keys = Rails.cache.fetch("atomic_admin_jwks") do + jwks_raw = Net::HTTP.get URI(AtomicAdmin.admin_jwks_url) + JSON.parse(jwks_raw) + end + + JWT::JWK::Set.new(keys).select { |k| k[:use] == "sig" } + end + JWT.decode( token, - secret || Rails.application.secrets.auth0_client_secret, + nil, validate, - { algorithm: algorithm }, + { algorithms: [algorithm], jwks: load_admin_jwks }, ) end def decoded_jwt_token(req, secret = nil) - token = AtomicAdmin::JwtToken.valid?(encoded_token(req), secret) + token = AtomicAdmin::JwtToken.valid?(encoded_token(req)) raise InvalidTokenError, "Unable to decode jwt token" if token.blank? raise InvalidTokenError, "Invalid token payload" if token.empty? token[0] end - + def validate_token return if @admin_app_validated token = decoded_jwt_token(request) - raise InvalidTokenError if Rails.application.secrets.auth0_client_id != token["aud"] - + raise InvalidTokenError if AtomicAdmin.audience != token["aud"] + current_application_instance_id = request.env['atomic.validated.application_instance_id'] if current_application_instance_id && current_application_instance_id != token["application_instance_id"] raise InvalidTokenError end - + @user_tenant = token["user_tenant"] if token["user_tenant"].present? @user = User.find(token["user_id"]) - + sign_in(@user, event: :authentication, store: false) rescue JWT::DecodeError, InvalidTokenError => e Rails.logger.error "JWT Error occured #{e.inspect}" begin render json: { error: "Unauthorized: Invalid token." }, status: :unauthorized rescue NoMethodError - raise GraphQL::ExecutionError, "Unauthorized: Invalid token." + raise RuntimeError, "Unauthorized: Invalid token." end end def validate_admin_app_token _bearer, jwt = request.headers['Authorization'].split(' ') - @atomic_admin_params = AtomicAdmin::JwtToken.decode(jwt, Rails.application.secrets.atomic_admin_shared_key) + @atomic_admin_params = AtomicAdmin::JwtToken.decode(jwt) @admin_app_validated = true rescue JWT::DecodeError, InvalidTokenError => e # fall back to regular app jwt Rails.logger.error "JWT Error occured with admin app token #{e.inspect}" @admin_app_validated = false end - + protected - + def encoded_token(req) return req.params[:jwt] if req.params[:jwt] - + header = req.headers["Authorization"] || req.headers[:authorization] raise InvalidTokenError, "No authorization header found" if header.nil? - + token = header.split(" ").last raise InvalidTokenError, "Invalid authorization header string" if token.nil? - - token - end - def load_admin_jwks(options) - jwks_raw = Net::HTTP.get URI("https://localhost:3000/jwks.json") # TODO: make this configurable - jwks_keys = JSON.parse(jwks_raw) - - JWT::JWK::Set.new(jwks_keys).select { |k| k[:use] == "sig" } + token end end end From 71e599167c10974763248e9e05d19cb99ea99a82 Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Fri, 25 Aug 2023 18:29:46 -0600 Subject: [PATCH 03/61] Implemented JWK token validation for the new Admin App, reworked the code to accomodate for it better --- .../atomic_admin/application_controller.rb | 20 ++-- app/controllers/concerns/.keep | 0 app/controllers/concerns/require_jwt_token.rb | 63 +++++++++++++ lib/atomic_admin.rb | 1 + lib/atomic_admin/jwt_token.rb | 92 +------------------ lib/atomic_admin/jwt_token/jwks_decoder.rb | 42 +++++++++ lib/atomic_admin/jwt_token/secret_decoder.rb | 29 ++++++ 7 files changed, 147 insertions(+), 100 deletions(-) delete mode 100644 app/controllers/concerns/.keep create mode 100644 app/controllers/concerns/require_jwt_token.rb create mode 100644 lib/atomic_admin/jwt_token/jwks_decoder.rb create mode 100644 lib/atomic_admin/jwt_token/secret_decoder.rb diff --git a/app/controllers/atomic_admin/application_controller.rb b/app/controllers/atomic_admin/application_controller.rb index 3bc7634..74032ea 100644 --- a/app/controllers/atomic_admin/application_controller.rb +++ b/app/controllers/atomic_admin/application_controller.rb @@ -1,21 +1,17 @@ module AtomicAdmin class ApplicationController < ActionController::API - include AtomicAdmin::JwtToken - # before_action :authenticate_user! # Use validate_token instead for now - before_action :validate_admin_app_token - before_action :validate_token - before_action :only_admins! + include RequireJwtToken + before_action :only_admins! - private + private - def only_admins! - return if @admin_app_validated - - user_not_authorized unless current_user.admin? - end + def only_admins! + return if @admin_app_validated + user_not_authorized unless current_user.present? && current_user.admin? + end - def user_not_authorized(message = "Not Authorized") + def user_not_authorized(message = "Not Authorized") render json: { message: message, }, status: 401 end diff --git a/app/controllers/concerns/.keep b/app/controllers/concerns/.keep deleted file mode 100644 index e69de29..0000000 diff --git a/app/controllers/concerns/require_jwt_token.rb b/app/controllers/concerns/require_jwt_token.rb new file mode 100644 index 0000000..ff57939 --- /dev/null +++ b/app/controllers/concerns/require_jwt_token.rb @@ -0,0 +1,63 @@ +module RequireJwtToken + extend ActiveSupport::Concern + + included do + before_action :validate_token + end + + protected + + def validate_token + encoded_token = get_encoded_token(request) + token = validate_admin_token(encoded_token) + if token.present? + @admin_app_validated = true + token + else + validate_internal_token(encoded_token) + end + end + + def validate_admin_token(encoded_token) + decoder = AtomicAdmin::JwtToken::JwksDecoder.new(AtomicAdmin.admin_jwks_url) + decoder.decode(encoded_token)&.first + end + + def validate_internal_token(encoded_token) + decoder = AtomicAdmin::JwtToken::SecretDecoder.new(AtomicAdmin.internal_secret) + token = decoder.decode!(request) + + if AtomicAdmin.audience != token["aud"] + raise AtomicAdmin::JwtToken::InvalidTokenError, "Invalid audience" + end + + current_application_instance_id = request.env['atomic.validated.application_instance_id'] + if current_application_instance_id && current_application_instance_id != token["application_instance_id"] + raise AtomicAdmin::JwtToken::InvalidTokenError, "Invalid application instance id" + end + + @user_tenant = token["user_tenant"] if token["user_tenant"].present? + @user = User.find(token["user_id"]) + + sign_in(@user, event: :authentication, store: false) + rescue JWT::DecodeError, AtomicAdmin::JwtToken::InvalidTokenError => e + Rails.logger.error "JWT Error occured #{e.inspect}" + begin + render json: { error: "Unauthorized: Invalid token." }, status: :unauthorized + rescue NoMethodError + raise RuntimeError, "Unauthorized: Invalid token." + end + end + + def get_encoded_token(req) + return req.params[:jwt] if req.params[:jwt] + + header = req.headers["Authorization"] || req.headers[:authorization] + raise AtomicAdmin::JwtToken::MissingTokenError, "No authorization header found" if header.nil? + + token = header.split(" ").last + raise AtomicAdmin::JwtToken::MissingTokenError, "Invalid authorization header string" if token.nil? + + token + end +end diff --git a/lib/atomic_admin.rb b/lib/atomic_admin.rb index 5e94127..dbc8941 100644 --- a/lib/atomic_admin.rb +++ b/lib/atomic_admin.rb @@ -5,4 +5,5 @@ module AtomicAdmin mattr_accessor :admin_jwks_url mattr_accessor :audience + mattr_accessor :internal_secret end diff --git a/lib/atomic_admin/jwt_token.rb b/lib/atomic_admin/jwt_token.rb index 220fd6e..00d972d 100644 --- a/lib/atomic_admin/jwt_token.rb +++ b/lib/atomic_admin/jwt_token.rb @@ -1,93 +1,9 @@ -## Note: This code is basically copied out of the starter app to authenticate -## admin app api calls. Lives at /app/controllers/concerns/jwt_token.rb in -## starter app +require_relative 'jwt_token/jwks_decoder' +require_relative 'jwt_token/secret_decoder' + module AtomicAdmin module JwtToken - - ALGORITHM = "RS256".freeze - class InvalidTokenError < StandardError; end - - def self.valid?(token, algorithm = ALGORITHM) - decode(token, secret, true, algorithm) - end - - def self.decode(token, validate = true, algorithm = ALGORITHM) - - load_admin_jwks = ->(options) do - Rails.cache.delete("atomic_admin_jwks") if options[:kid_not_found] - - # NOTE: the cached keys only expire when we recieve a kid_not_found error - keys = Rails.cache.fetch("atomic_admin_jwks") do - jwks_raw = Net::HTTP.get URI(AtomicAdmin.admin_jwks_url) - JSON.parse(jwks_raw) - end - - JWT::JWK::Set.new(keys).select { |k| k[:use] == "sig" } - end - - JWT.decode( - token, - nil, - validate, - { algorithms: [algorithm], jwks: load_admin_jwks }, - ) - end - - def decoded_jwt_token(req, secret = nil) - token = AtomicAdmin::JwtToken.valid?(encoded_token(req)) - raise InvalidTokenError, "Unable to decode jwt token" if token.blank? - raise InvalidTokenError, "Invalid token payload" if token.empty? - - token[0] - end - - def validate_token - return if @admin_app_validated - - token = decoded_jwt_token(request) - raise InvalidTokenError if AtomicAdmin.audience != token["aud"] - - current_application_instance_id = request.env['atomic.validated.application_instance_id'] - if current_application_instance_id && current_application_instance_id != token["application_instance_id"] - raise InvalidTokenError - end - - @user_tenant = token["user_tenant"] if token["user_tenant"].present? - @user = User.find(token["user_id"]) - - sign_in(@user, event: :authentication, store: false) - rescue JWT::DecodeError, InvalidTokenError => e - Rails.logger.error "JWT Error occured #{e.inspect}" - begin - render json: { error: "Unauthorized: Invalid token." }, status: :unauthorized - rescue NoMethodError - raise RuntimeError, "Unauthorized: Invalid token." - end - end - - def validate_admin_app_token - _bearer, jwt = request.headers['Authorization'].split(' ') - @atomic_admin_params = AtomicAdmin::JwtToken.decode(jwt) - @admin_app_validated = true - rescue JWT::DecodeError, InvalidTokenError => e - # fall back to regular app jwt - Rails.logger.error "JWT Error occured with admin app token #{e.inspect}" - @admin_app_validated = false - end - - protected - - def encoded_token(req) - return req.params[:jwt] if req.params[:jwt] - - header = req.headers["Authorization"] || req.headers[:authorization] - raise InvalidTokenError, "No authorization header found" if header.nil? - - token = header.split(" ").last - raise InvalidTokenError, "Invalid authorization header string" if token.nil? - - token - end + class MissingTokenError < StandardError; end end end diff --git a/lib/atomic_admin/jwt_token/jwks_decoder.rb b/lib/atomic_admin/jwt_token/jwks_decoder.rb new file mode 100644 index 0000000..6ea2f15 --- /dev/null +++ b/lib/atomic_admin/jwt_token/jwks_decoder.rb @@ -0,0 +1,42 @@ +module AtomicAdmin::JwtToken + # Decodes a JWT token using the JWKS endpoint + # This is used for decoding JWT tokens issued by the new + # admin app + class JwksDecoder + ALGORITHMS = ["RS256"].freeze + + def initialize(jwks_url, algorithms = ALGORITHMS) + @jwks_url = jwks_url + @algorithms = algorithms + end + + def decode(token, validate = true) + load_admin_jwks = ->(options) do + Rails.cache.delete("atomic_admin_jwks") if options[:kid_not_found] + + # NOTE: the cached keys only expire when we recieve a kid_not_found error + keys = Rails.cache.fetch("atomic_admin_jwks") do + jwks_raw = Net::HTTP.get URI(@jwks_url) + JSON.parse(jwks_raw) + end + + JWT::JWK::Set.new(keys).select { |k| k[:use] == "sig" } + end + + JWT.decode( + token, + nil, + validate, + { algorithms: @algorithms, jwks: load_admin_jwks }, + ) + end + + def decode!(token) + token = decode(token) + raise AtomicAdmin::JwtToken::InvalidTokenError, "Unable to decode jwt token" if token.blank? + raise AtomicAdmin::JwtToken::InvalidTokenError, "Invalid token payload" if token.empty? + + token[0] + end + end +end diff --git a/lib/atomic_admin/jwt_token/secret_decoder.rb b/lib/atomic_admin/jwt_token/secret_decoder.rb new file mode 100644 index 0000000..00b9214 --- /dev/null +++ b/lib/atomic_admin/jwt_token/secret_decoder.rb @@ -0,0 +1,29 @@ +module AtomicAdmin::JwtToken + # Decodes a JWT token using a known secret. This is used for decoding + # JWT tokens issued by the application itself for the old admin app + class SecretDecoder + ALGORITHM = "HS512".freeze + + def initialize(secret, algorithm = ALGORITHM) + @secret = secret + @algorithm = algorithm + end + + def decode(token, validate = true) + JWT.decode( + token, + @secret, + validate, + { algorithm: @algorithm }, + ) + end + + def decode!(token) + token = decode(token) + raise AtomicAdmin::JwtToken::InvalidTokenError, "Unable to decode jwt token" if token.blank? + raise AtomicAdmin::JwtToken::InvalidTokenError, "Invalid token payload" if token.empty? + + token[0] + end + end +end From 33e204b6eea4e782a2b80cc8e9e7c3c8e07f6a00 Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Fri, 1 Sep 2023 12:11:33 -0600 Subject: [PATCH 04/61] Fixed internal token validation --- app/controllers/concerns/require_jwt_token.rb | 52 ++++++++++--------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/app/controllers/concerns/require_jwt_token.rb b/app/controllers/concerns/require_jwt_token.rb index ff57939..47cbe18 100644 --- a/app/controllers/concerns/require_jwt_token.rb +++ b/app/controllers/concerns/require_jwt_token.rb @@ -2,34 +2,32 @@ module RequireJwtToken extend ActiveSupport::Concern included do - before_action :validate_token + before_action :validate_admin_token + before_action :validate_internal_token end protected - def validate_token + def validate_admin_token encoded_token = get_encoded_token(request) - token = validate_admin_token(encoded_token) - if token.present? - @admin_app_validated = true - token - else - validate_internal_token(encoded_token) - end - end - - def validate_admin_token(encoded_token) decoder = AtomicAdmin::JwtToken::JwksDecoder.new(AtomicAdmin.admin_jwks_url) - decoder.decode(encoded_token)&.first + token = decoder.decode(encoded_token)&.first + validate_claims!(token) + @admin_app_validated = true + token + rescue Exception => e + # Capture all exceptions to let the internal token validation handle it + Rails.logger.error "Admin JWT Error occured #{e.inspect}" + nil end - def validate_internal_token(encoded_token) - decoder = AtomicAdmin::JwtToken::SecretDecoder.new(AtomicAdmin.internal_secret) - token = decoder.decode!(request) + def validate_internal_token + return if @admin_app_validated - if AtomicAdmin.audience != token["aud"] - raise AtomicAdmin::JwtToken::InvalidTokenError, "Invalid audience" - end + encoded_token = get_encoded_token(request) + decoder = AtomicAdmin::JwtToken::SecretDecoder.new(AtomicAdmin.internal_secret) + token = decoder.decode!(encoded_token) + validate_claims!(token) current_application_instance_id = request.env['atomic.validated.application_instance_id'] if current_application_instance_id && current_application_instance_id != token["application_instance_id"] @@ -41,14 +39,12 @@ def validate_internal_token(encoded_token) sign_in(@user, event: :authentication, store: false) rescue JWT::DecodeError, AtomicAdmin::JwtToken::InvalidTokenError => e - Rails.logger.error "JWT Error occured #{e.inspect}" - begin - render json: { error: "Unauthorized: Invalid token." }, status: :unauthorized - rescue NoMethodError - raise RuntimeError, "Unauthorized: Invalid token." - end + Rails.logger.error "Internal JWT Error occured #{e.inspect}" + render json: { error: "Unauthorized: Invalid token." }, status: :unauthorized end + private + def get_encoded_token(req) return req.params[:jwt] if req.params[:jwt] @@ -60,4 +56,10 @@ def get_encoded_token(req) token end + + def validate_claims!(token) + if AtomicAdmin.audience != token["aud"] + raise AtomicAdmin::JwtToken::InvalidTokenError, "Expected audience to be #{AtomicAdmin.audience} but was #{token["aud"]}" + end + end end From 0aac772ff98fca21e0db25fe12c1456e2b3f55a8 Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Fri, 1 Sep 2023 12:12:49 -0600 Subject: [PATCH 05/61] inverted admin conditional check --- app/controllers/atomic_admin/application_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/atomic_admin/application_controller.rb b/app/controllers/atomic_admin/application_controller.rb index 74032ea..721c034 100644 --- a/app/controllers/atomic_admin/application_controller.rb +++ b/app/controllers/atomic_admin/application_controller.rb @@ -8,7 +8,7 @@ class ApplicationController < ActionController::API def only_admins! return if @admin_app_validated - user_not_authorized unless current_user.present? && current_user.admin? + user_not_authorized if current_user.blank? && !current_user.admin? end def user_not_authorized(message = "Not Authorized") From 6a031d9095674cae95e3bf925ed2c40b0003c610 Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Fri, 1 Sep 2023 12:17:18 -0600 Subject: [PATCH 06/61] added some helper methods to check how the request is authorized --- .../atomic_admin/application_controller.rb | 2 +- app/controllers/concerns/require_jwt_token.rb | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/app/controllers/atomic_admin/application_controller.rb b/app/controllers/atomic_admin/application_controller.rb index 721c034..6a493d2 100644 --- a/app/controllers/atomic_admin/application_controller.rb +++ b/app/controllers/atomic_admin/application_controller.rb @@ -6,7 +6,7 @@ class ApplicationController < ActionController::API private def only_admins! - return if @admin_app_validated + return if is_atomic_admin? user_not_authorized if current_user.blank? && !current_user.admin? end diff --git a/app/controllers/concerns/require_jwt_token.rb b/app/controllers/concerns/require_jwt_token.rb index 47cbe18..0df91f9 100644 --- a/app/controllers/concerns/require_jwt_token.rb +++ b/app/controllers/concerns/require_jwt_token.rb @@ -2,10 +2,20 @@ module RequireJwtToken extend ActiveSupport::Concern included do + attr_accessor :auth_source + before_action :validate_admin_token before_action :validate_internal_token end + def is_atomic_admin? + self.auth_source == :atomic_admin + end + + def is_internal? + self.auth_source == :internal + end + protected def validate_admin_token @@ -13,7 +23,8 @@ def validate_admin_token decoder = AtomicAdmin::JwtToken::JwksDecoder.new(AtomicAdmin.admin_jwks_url) token = decoder.decode(encoded_token)&.first validate_claims!(token) - @admin_app_validated = true + self.auth_source = :atomic_admin + token rescue Exception => e # Capture all exceptions to let the internal token validation handle it @@ -22,7 +33,7 @@ def validate_admin_token end def validate_internal_token - return if @admin_app_validated + return if is_atomic_admin? encoded_token = get_encoded_token(request) decoder = AtomicAdmin::JwtToken::SecretDecoder.new(AtomicAdmin.internal_secret) @@ -38,6 +49,7 @@ def validate_internal_token @user = User.find(token["user_id"]) sign_in(@user, event: :authentication, store: false) + self.auth_source = :internal rescue JWT::DecodeError, AtomicAdmin::JwtToken::InvalidTokenError => e Rails.logger.error "Internal JWT Error occured #{e.inspect}" render json: { error: "Unauthorized: Invalid token." }, status: :unauthorized From f0d71cf5d2d1f7ca1a473845de1775e1576c0840 Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Fri, 5 Apr 2024 21:52:30 -0600 Subject: [PATCH 07/61] Application listing / editing working Application instance listing workings --- Gemfile.lock | 4 + .../atomic_admin/application_controller.rb | 14 +++- ...atomic_application_instances_controller.rb | 81 +++++++++++++++++++ .../atomic_applications_controller.rb | 30 +++++++ config/routes.rb | 5 +- ...c_application_instances_controller_test.rb | 11 +++ .../atomic_applications_controller_test.rb | 11 +++ 7 files changed, 154 insertions(+), 2 deletions(-) create mode 100644 app/controllers/atomic_admin/atomic_application_instances_controller.rb create mode 100644 app/controllers/atomic_admin/atomic_applications_controller.rb create mode 100644 test/controllers/atomic_admin/atomic_admin/atomic_application_instances_controller_test.rb create mode 100644 test/controllers/atomic_admin/atomic_admin/atomic_applications_controller_test.rb diff --git a/Gemfile.lock b/Gemfile.lock index e1b9742..17af40b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -109,6 +109,8 @@ GEM net-smtp (0.3.3) net-protocol nio4r (2.5.9) + nokogiri (1.15.4-arm64-darwin) + racc (~> 1.4) nokogiri (1.15.4-x86_64-darwin) racc (~> 1.4) pg (1.5.3) @@ -152,6 +154,7 @@ GEM actionpack (>= 5.2) activesupport (>= 5.2) sprockets (>= 3.0.0) + sqlite3 (1.6.3-arm64-darwin) sqlite3 (1.6.3-x86_64-darwin) thor (1.2.2) timeout (0.4.0) @@ -163,6 +166,7 @@ GEM zeitwerk (2.6.11) PLATFORMS + arm64-darwin-22 x86_64-darwin-22 DEPENDENCIES diff --git a/app/controllers/atomic_admin/application_controller.rb b/app/controllers/atomic_admin/application_controller.rb index 6a493d2..1f51890 100644 --- a/app/controllers/atomic_admin/application_controller.rb +++ b/app/controllers/atomic_admin/application_controller.rb @@ -3,8 +3,21 @@ class ApplicationController < ActionController::API include RequireJwtToken before_action :only_admins! + rescue_from ActiveRecord::RecordNotFound, with: :record_not_found + def record_not_found + render json: { message: "Record not found" }, status: 404 + end + private + def json_for(resource) + resource.as_json + end + + def json_for_collection(collection) + collection.map { |resource| json_for(resource) } + end + def only_admins! return if is_atomic_admin? @@ -14,6 +27,5 @@ def only_admins! def user_not_authorized(message = "Not Authorized") render json: { message: message, }, status: 401 end - end end diff --git a/app/controllers/atomic_admin/atomic_application_instances_controller.rb b/app/controllers/atomic_admin/atomic_application_instances_controller.rb new file mode 100644 index 0000000..b3d5d36 --- /dev/null +++ b/app/controllers/atomic_admin/atomic_application_instances_controller.rb @@ -0,0 +1,81 @@ +module AtomicAdmin + class AtomicApplicationInstancesController < ApplicationController + + def index + @application_instances = ApplicationInstance.joins(:application).where(application: { kind: :lti }) + @application_instances = + if type == "paid" + @application_instances.where.not(paid_at: nil) + else + @application_instances.where(paid_at: nil) + end + + if search.present? + @application_instances = @application_instances. + where("nickname LIKE ?", "%#{search}%"). + or(@application_instances.where("lti_key LIKE ?", "%#{search}%")) + end + + order_by = + if sort_column === "nickname" + "LOWER(#{sort_column}) #{sort_direction}" + else + { sort_column.to_sym => sort_direction.to_sym } + end + + @application_instances = @application_instances. + order(order_by). + paginate(page: params[:page], per_page: 30) + + render json: { + application_instances: json_for_collection(@application_instances), + meta: { + current_page: @application_instances.current_page, + next_page: @application_instances.next_page, + prev_page: @application_instances.previous_page, + total_pages: @application_instances.total_pages, + } + } + end + + private + + def json_for(instance) + json = instance.as_json + json[:site] = instance.site.as_json + json[:application] = instance.application.as_json + + json + end + + def sortable_columns + [ + "created_at", + "trial_end_date", + "trial_users", + "license_end_date", + "licensed_users", + "nickname", + ] + end + + def sort_column + sortable_columns.include?(params[:column]) ? params[:column] : "created_at" + end + + def sort_direction + { + "ascending" => "asc", + "descending" => "desc", + }.fetch(params[:direction], "desc") + end + + def type + params[:type] == "paid" ? "paid" : "evals" + end + + def search + params[:search] + end + end +end diff --git a/app/controllers/atomic_admin/atomic_applications_controller.rb b/app/controllers/atomic_admin/atomic_applications_controller.rb new file mode 100644 index 0000000..c77bc9d --- /dev/null +++ b/app/controllers/atomic_admin/atomic_applications_controller.rb @@ -0,0 +1,30 @@ +module AtomicAdmin + class AtomicApplicationsController < ApplicationController + def index + @applications = Application.all + render json: { applications: json_for_collection(@applications.lti) } + end + + def show + @application = Application.find(params[:id]) + render json: { application: json_for(@application) } + end + + def update + @application = Application.find(params[:id]) + + # Strong params doesn't allow abritrary json, so we need to set the values manually + @application.default_config = params[:default_config] + @application.canvas_api_permissions = params[:canvas_api_permissions] + + @application.update!(application_params) + render json: { application: json_for(@application) } + end + + private + + def application_params + params.permit(:name, :description, :oauth_key, :oauth_secret) + end + end +end diff --git a/config/routes.rb b/config/routes.rb index 47f842e..c6cc588 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,5 +1,5 @@ AtomicAdmin::Engine.routes.draw do - # namespace :lti do + # namespace :lti do resources :atomic_lti_platform resources :atomic_lti_install resources :atomic_tenant_deployment @@ -10,4 +10,7 @@ post '/atomic_tenant_client_id_strategy/search', to: 'atomic_tenant_client_id_strategy#search' resources :atomic_tenant_client_id_strategy + + resources :atomic_applications + resources :atomic_application_instances end diff --git a/test/controllers/atomic_admin/atomic_admin/atomic_application_instances_controller_test.rb b/test/controllers/atomic_admin/atomic_admin/atomic_application_instances_controller_test.rb new file mode 100644 index 0000000..88c913a --- /dev/null +++ b/test/controllers/atomic_admin/atomic_admin/atomic_application_instances_controller_test.rb @@ -0,0 +1,11 @@ +require "test_helper" + +module AtomicAdmin + class AtomicAdmin::AtomicApplicationInstancesControllerTest < ActionDispatch::IntegrationTest + include Engine.routes.url_helpers + + # test "the truth" do + # assert true + # end + end +end diff --git a/test/controllers/atomic_admin/atomic_admin/atomic_applications_controller_test.rb b/test/controllers/atomic_admin/atomic_admin/atomic_applications_controller_test.rb new file mode 100644 index 0000000..c35046f --- /dev/null +++ b/test/controllers/atomic_admin/atomic_admin/atomic_applications_controller_test.rb @@ -0,0 +1,11 @@ +require "test_helper" + +module AtomicAdmin + class AtomicAdmin::AtomicApplicationsControllerTest < ActionDispatch::IntegrationTest + include Engine.routes.url_helpers + + # test "the truth" do + # assert true + # end + end +end From a38a7bffdaa97418251d68af8d1efe29c197d6f6 Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Sat, 6 Apr 2024 13:43:12 -0600 Subject: [PATCH 08/61] Application Update schema --- .../atomic_applications_controller.rb | 4 + config/routes.rb | 4 +- lib/atomic_admin.rb | 1 + lib/atomic_admin/schema.rb | 15 +++ .../atomic_application_update_schema.rb | 91 +++++++++++++++++++ 5 files changed, 114 insertions(+), 1 deletion(-) create mode 100644 lib/atomic_admin/schema.rb create mode 100644 lib/atomic_admin/schema/atomic_application_update_schema.rb diff --git a/app/controllers/atomic_admin/atomic_applications_controller.rb b/app/controllers/atomic_admin/atomic_applications_controller.rb index c77bc9d..8014a0f 100644 --- a/app/controllers/atomic_admin/atomic_applications_controller.rb +++ b/app/controllers/atomic_admin/atomic_applications_controller.rb @@ -21,6 +21,10 @@ def update render json: { application: json_for(@application) } end + def update_schema + render json: AtomicAdmin::Schema.for(Application.find(params[:atomic_application_id])) + end + private def application_params diff --git a/config/routes.rb b/config/routes.rb index c6cc588..3796bbb 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -11,6 +11,8 @@ resources :atomic_tenant_client_id_strategy - resources :atomic_applications + resources :atomic_applications do + get "update_schema", to: "atomic_applications#update_schema" + end resources :atomic_application_instances end diff --git a/lib/atomic_admin.rb b/lib/atomic_admin.rb index dbc8941..cd4dd4f 100644 --- a/lib/atomic_admin.rb +++ b/lib/atomic_admin.rb @@ -1,6 +1,7 @@ require "atomic_admin/version" require "atomic_admin/engine" require "atomic_admin/jwt_token" +require "atomic_admin/schema" module AtomicAdmin mattr_accessor :admin_jwks_url diff --git a/lib/atomic_admin/schema.rb b/lib/atomic_admin/schema.rb new file mode 100644 index 0000000..523ad60 --- /dev/null +++ b/lib/atomic_admin/schema.rb @@ -0,0 +1,15 @@ + +require_relative "schema/atomic_application_update_schema" + +module AtomicAdmin + module Schema + def self.for(resource) + case resource.class.name + when "Application" + AtomicApplicationUpdateSchema.for(resource) + else + raise "Unknown resource type: #{resource.class.name}" + end + end + end +end diff --git a/lib/atomic_admin/schema/atomic_application_update_schema.rb b/lib/atomic_admin/schema/atomic_application_update_schema.rb new file mode 100644 index 0000000..b08783f --- /dev/null +++ b/lib/atomic_admin/schema/atomic_application_update_schema.rb @@ -0,0 +1,91 @@ + +module AtomicAdmin::Schema + class AtomicApplicationUpdateSchema + attr_accessor :application + + def self.for(application) + new(application).call + end + + def initialize(application) + @application = application + end + + def call + { + schema: schema, + uischema: uischema, + } + end + + def schema + { + type: "object", + properties: { + description: { + type: "string", + }, + oauth_key: { + type: ["string", "null"], + }, + oauth_secret: { + type: ["string", "null"], + }, + default_config: { + type: "object", + }, + canvas_api_permissions: { + type: "object", + }, + } + } + end + + def uischema + { + type: "VerticalLayout", + elements: [ + { + type: "Control", + scope: "#/properties/description", + options: { + format: "textarea", + props: { + size: "full", + resize: "vertical" + } + } + }, + { + type: "Control", + scope: "#/properties/oauth_key", + }, + { + type: "Control", + scope: "#/properties/oauth_secret", + }, + { + type: "Control", + scope: "#/properties/default_config", + options: { + format: "json", + props: { + size: "full" + } + } + }, + { + type: "Control", + scope: "#/properties/canvas_api_permissions", + options: { + format: "json", + props: { + size: "full" + } + } + }, + ] + } + end + end +end From 8a2ba9d9d51feda26135776f0f5a1ee492bb5e3f Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Thu, 11 Apr 2024 09:08:36 -0600 Subject: [PATCH 09/61] Updates --- .../atomic_application_instances_controller.rb | 2 +- .../atomic_admin/atomic_applications_controller.rb | 11 +++++++++-- config/routes.rb | 3 ++- lib/atomic_admin/schema.rb | 6 +++--- 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/app/controllers/atomic_admin/atomic_application_instances_controller.rb b/app/controllers/atomic_admin/atomic_application_instances_controller.rb index b3d5d36..95c9356 100644 --- a/app/controllers/atomic_admin/atomic_application_instances_controller.rb +++ b/app/controllers/atomic_admin/atomic_application_instances_controller.rb @@ -2,7 +2,7 @@ module AtomicAdmin class AtomicApplicationInstancesController < ApplicationController def index - @application_instances = ApplicationInstance.joins(:application).where(application: { kind: :lti }) + @application_instances = ApplicationInstance.where(application_id: params[:atomic_application_id]) @application_instances = if type == "paid" @application_instances.where.not(paid_at: nil) diff --git a/app/controllers/atomic_admin/atomic_applications_controller.rb b/app/controllers/atomic_admin/atomic_applications_controller.rb index 8014a0f..2ad9a5f 100644 --- a/app/controllers/atomic_admin/atomic_applications_controller.rb +++ b/app/controllers/atomic_admin/atomic_applications_controller.rb @@ -1,3 +1,7 @@ +# How do we handle customization of these endpoints? +# 1. The tools override the controller and add the custom logic +# 2. The controllers call into the resource's class with the payloads and it handles the details of the implementation + module AtomicAdmin class AtomicApplicationsController < ApplicationController def index @@ -7,7 +11,7 @@ def index def show @application = Application.find(params[:id]) - render json: { application: json_for(@application) } + render json: { application: json_for(@application) } end def update @@ -22,7 +26,10 @@ def update end def update_schema - render json: AtomicAdmin::Schema.for(Application.find(params[:atomic_application_id])) + render json: AtomicAdmin::Schema.for( + Application.find(params[:atomic_application_id]), + "update" + ) end private diff --git a/config/routes.rb b/config/routes.rb index 3796bbb..591a105 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -13,6 +13,7 @@ resources :atomic_applications do get "update_schema", to: "atomic_applications#update_schema" + + resources :atomic_application_instances end - resources :atomic_application_instances end diff --git a/lib/atomic_admin/schema.rb b/lib/atomic_admin/schema.rb index 523ad60..ab5e47a 100644 --- a/lib/atomic_admin/schema.rb +++ b/lib/atomic_admin/schema.rb @@ -3,9 +3,9 @@ module AtomicAdmin module Schema - def self.for(resource) - case resource.class.name - when "Application" + def self.for(resource, type) + case [resource.class.name, type] + when ["Application", "update"] AtomicApplicationUpdateSchema.for(resource) else raise "Unknown resource type: #{resource.class.name}" From 8b888ae28605bcea8d28c5cacadc264015919a08 Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Tue, 30 Apr 2024 18:29:56 -0600 Subject: [PATCH 10/61] Retrieving Application Instances is working --- .../atomic_admin/atomic_application_instances_controller.rb | 5 +++++ lib/atomic_admin/schema.rb | 4 +++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/app/controllers/atomic_admin/atomic_application_instances_controller.rb b/app/controllers/atomic_admin/atomic_application_instances_controller.rb index 95c9356..272cb82 100644 --- a/app/controllers/atomic_admin/atomic_application_instances_controller.rb +++ b/app/controllers/atomic_admin/atomic_application_instances_controller.rb @@ -38,6 +38,11 @@ def index } end + def show + @application_instance = ApplicationInstance.find(params[:id]) + render json: { application_instance: json_for(@application_instance) } + end + private def json_for(instance) diff --git a/lib/atomic_admin/schema.rb b/lib/atomic_admin/schema.rb index ab5e47a..28d24ee 100644 --- a/lib/atomic_admin/schema.rb +++ b/lib/atomic_admin/schema.rb @@ -6,7 +6,9 @@ module Schema def self.for(resource, type) case [resource.class.name, type] when ["Application", "update"] - AtomicApplicationUpdateSchema.for(resource) + schema = AtomicApplicationUpdateSchema.for(resource) + # schema = Application.update_schema(schema) + schema else raise "Unknown resource type: #{resource.class.name}" end From 98b493db6eb6f0beccea2ba18a86910ade9a2a9e Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Tue, 30 Apr 2024 19:04:40 -0600 Subject: [PATCH 11/61] Retrieving Interactions from Backend --- ...atomic_application_instances_controller.rb | 5 + config/routes.rb | 6 +- lib/atomic_admin.rb | 1 + lib/atomic_admin/interaction.rb | 3 + ...atomic_application_instance_interaction.rb | 430 ++++++++++++++++++ 5 files changed, 444 insertions(+), 1 deletion(-) create mode 100644 lib/atomic_admin/interaction.rb create mode 100644 lib/atomic_admin/interaction/atomic_application_instance_interaction.rb diff --git a/app/controllers/atomic_admin/atomic_application_instances_controller.rb b/app/controllers/atomic_admin/atomic_application_instances_controller.rb index 272cb82..a104d97 100644 --- a/app/controllers/atomic_admin/atomic_application_instances_controller.rb +++ b/app/controllers/atomic_admin/atomic_application_instances_controller.rb @@ -43,6 +43,11 @@ def show render json: { application_instance: json_for(@application_instance) } end + def interactions + interactions = AtomicAdmin::Interaction::AtomicApplicationInstanceInteraction.get + render json: { interactions: interactions } + end + private def json_for(instance) diff --git a/config/routes.rb b/config/routes.rb index 591a105..f20e6c4 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -14,6 +14,10 @@ resources :atomic_applications do get "update_schema", to: "atomic_applications#update_schema" - resources :atomic_application_instances + resources :atomic_application_instances do + member do + get :interactions + end + end end end diff --git a/lib/atomic_admin.rb b/lib/atomic_admin.rb index cd4dd4f..9835338 100644 --- a/lib/atomic_admin.rb +++ b/lib/atomic_admin.rb @@ -2,6 +2,7 @@ require "atomic_admin/engine" require "atomic_admin/jwt_token" require "atomic_admin/schema" +require "atomic_admin/interaction" module AtomicAdmin mattr_accessor :admin_jwks_url diff --git a/lib/atomic_admin/interaction.rb b/lib/atomic_admin/interaction.rb new file mode 100644 index 0000000..244a223 --- /dev/null +++ b/lib/atomic_admin/interaction.rb @@ -0,0 +1,3 @@ +module AtomicAdmin::Interaction + require_relative "interaction/atomic_application_instance_interaction" +end diff --git a/lib/atomic_admin/interaction/atomic_application_instance_interaction.rb b/lib/atomic_admin/interaction/atomic_application_instance_interaction.rb new file mode 100644 index 0000000..709f6cc --- /dev/null +++ b/lib/atomic_admin/interaction/atomic_application_instance_interaction.rb @@ -0,0 +1,430 @@ + +module AtomicAdmin::Interaction::AtomicApplicationInstanceInteraction + def self.get + sites = Site.all + + [ + { + type: "analytics", + title: "Overview", + icon: "bar_chart", + key: "analytics", + }, + { + type: "jsonform", + title: "General Settings", + icon: "edit_note", + key: "generalSettings", + schema: { + type: "object", + properties: { + nickname: { + type: "string", + minLength: 1, + }, + primary_contact: { + type: ["string", "null"], + }, + created_at: { + type: "string", + format: "date-time", + }, + lti_key: { + type: "string", + }, + lti_secret: { + type: "string", + }, + # The available sites is based on the sites that have been created + # so this would require it to be dynamically generated on the fly + site_id: { + type: "string", + oneOf: sites.map do |site| + { + title: site.url, + const: site.id.to_s, + } + end + }, + domain: { + type: "string", + }, + canvas_token: { + type: ["string", "null"], + }, + rollbar_enabled: { + type: "boolean", + }, + use_scoped_developer_key: { + type: "boolean", + }, + }, + required: ["nickname"], + }, + uischema: { + type: "HorizontalLayout", + elements: [ + { + type: "Group", + label: "General Settings", + elements: [ + { + type: "VerticalLayout", + elements: [ + { + type: "Control", + scope: "#/properties/nickname", + }, + { + type: "Control", + scope: "#/properties/primary_contact", + }, + { + type: "Control", + scope: "#/properties/created_at", + label: "Date Created", + options: { + format: "date", + props: { + isReadOnly: true, + size: "small", + }, + }, + }, + { + type: "Control", + scope: "#/properties/site_id", + label: "LMS URL", + options: { + props: { + menuSize: "auto", + } + } + }, + { + type: "Control", + scope: "#/properties/domain", + label: "LTI Tool Domain", + }, + { + type: "Control", + scope: "#/properties/canvas_token", + label: "Canvas Token", + options: { + props: { + message: "Current Canvas Token: null", + }, + }, + }, + { + type: "Control", + scope: "#/properties/rollbar_enabled", + label: "Enable Rollbar", + }, + { + type: "Control", + scope: "#/properties/use_scoped_developer_key", + label: "Use Scoped Developer Key", + }, + ], + }, + ], + }, + { + type: "Group", + label: "LTI Key and Secret", + elements: [ + { + type: "VerticalLayout", + elements: [ + { + type: "Control", + scope: "#/properties/lti_key", + label: "LTI Key", + options: { + props: { + isReadOnly: true, + }, + }, + }, + { + type: "Control", + scope: "#/properties/lti_secret", + label: "LTI Secret", + }, + ], + }, + ], + }, + ], + }, + }, + { + type: "jsonform", + title: "Configuration", + icon: "settings", + key: "configuration", + schema: { + type: "object", + properties: { + config: { + type: "object", + }, + lti_config: { + type: "object", + }, + }, + }, + uischema: { + type: "VerticalLayout", + elements: [ + { + type: "Group", + label: "Custom App Instance Configuration", + elements: [ + { + type: "Control", + scope: "#/properties/config", + options: { + format: "json", + props: { + size: "full", + label: "", + height: "400px", + }, + }, + }, + ], + }, + { + type: "Group", + label: "LTI Config", + elements: [ + { + type: "Control", + scope: "#/properties/lti_config", + options: { + format: "json", + props: { + size: "full", + label: "", + height: "400px", + }, + }, + }, + ], + }, + ], + }, + }, + { + type: "jsonform", + title: "XML Configuration", + icon: "code", + key: "xmlConfiguration", + schema: { + type: "object", + properties: { + lti_key: { + type: "string", + }, + lti_secret: { + type: "string", + }, + lti_config_xml: { + type: "string", + }, + }, + }, + uischema: { + type: "Group", + label: "XML Configuration", + elements: [ + { + type: "VerticalLayout", + elements: [ + { + type: "Control", + scope: "#/properties/lti_key", + options: { + props: { + label: "LTI Key", + isReadOnly: true, + }, + }, + }, + { + type: "Control", + scope: "#/properties/lti_secret", + options: { + props: { + label: "LTI Secret", + isReadOnly: true, + }, + }, + }, + { + type: "Control", + scope: "#/properties/lti_config_xml", + options: { + format: "textarea", + props: { + label: "LTI Config XML", + isReadOnly: true, + }, + }, + }, + ], + }, + ], + }, + }, + { + type: "jsonform", + title: "License Details", + icon: "payments", + key: "licenseDetails", + schema: { + type: "object", + properties: { + license_start_date: { + type: ["string", "null"], + format: "date-time", + }, + license_end_date: { + type: ["string", "null"], + format: "date-time", + }, + licensed_users: { + type: ["number", "null"], + }, + license_type: { + type: ["string", "null"], + oneOf: [ + { + title: "Monthly", + const: "monthly", + }, + { + title: "Annual", + const: "annual", + }, + { + title: "FTE", + const: "fte", + }, + ], + }, + license_notes: { + type: ["string", "null"], + }, + }, + }, + uischema: { + type: "Group", + label: "License Details", + elements: [ + { + type: "VerticalLayout", + elements: [ + { + type: "Control", + scope: "#/properties/license_start_date", + options: { + format: "date", + }, + }, + { + type: "Control", + scope: "#/properties/license_end_date", + options: { + format: "date", + }, + }, + { + type: "Control", + scope: "#/properties/licensed_users", + }, + { + type: "Control", + scope: "#/properties/license_type", + options: { + format: "radio", + }, + }, + { + type: "Control", + scope: "#/properties/license_notes", + options: { + format: "textarea", + }, + }, + ], + }, + ], + }, + }, + { + type: "jsonform", + title: "Trial Details", + icon: "content_paste", + key: "trialDetails", + schema: { + type: "object", + properties: { + trial_start_date: { + type: ["string", "null"], + format: "date-time", + }, + trial_end_date: { + type: ["string", "null"], + format: "date-time", + }, + trial_users: { + type: ["number", "null"], + }, + trial_notes: { + type: ["string", "null"], + }, + }, + }, + uischema: { + type: "Group", + label: "Trial Details", + elements: [ + { + type: "VerticalLayout", + elements: [ + { + type: "Control", + scope: "#/properties/trial_start_date", + options: { + format: "date", + }, + }, + { + type: "Control", + scope: "#/properties/trial_end_date", + options: { + format: "date", + }, + }, + { + type: "Control", + scope: "#/properties/trial_users", + }, + { + type: "Control", + scope: "#/properties/trial_notes", + options: { + format: "textarea", + }, + }, + ], + }, + ], + }, + }, + ] + end +end From 973563493b50b99e980e0584fd1576a5c90d0acf Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Thu, 2 May 2024 13:24:47 -0600 Subject: [PATCH 12/61] Iniitial Integration manager implementation --- ...atomic_application_instances_controller.rb | 3 +- lib/atomic_admin/interaction.rb | 44 +- .../interaction/analytics_interaction.rb | 8 + ...atomic_application_instance_interaction.rb | 802 +++++++++--------- .../interaction/base_interaction.rb | 44 + .../general_settings_interaction.rb | 159 ++++ 6 files changed, 672 insertions(+), 388 deletions(-) create mode 100644 lib/atomic_admin/interaction/analytics_interaction.rb create mode 100644 lib/atomic_admin/interaction/base_interaction.rb create mode 100644 lib/atomic_admin/interaction/general_settings_interaction.rb diff --git a/app/controllers/atomic_admin/atomic_application_instances_controller.rb b/app/controllers/atomic_admin/atomic_application_instances_controller.rb index a104d97..48e1aa7 100644 --- a/app/controllers/atomic_admin/atomic_application_instances_controller.rb +++ b/app/controllers/atomic_admin/atomic_application_instances_controller.rb @@ -44,7 +44,8 @@ def show end def interactions - interactions = AtomicAdmin::Interaction::AtomicApplicationInstanceInteraction.get + instance = ApplicationInstance.find(params[:id]) + interactions = AtomicAdmin::Interaction::Manager.for(:application_instances, application_instance: instance) render json: { interactions: interactions } end diff --git a/lib/atomic_admin/interaction.rb b/lib/atomic_admin/interaction.rb index 244a223..6619917 100644 --- a/lib/atomic_admin/interaction.rb +++ b/lib/atomic_admin/interaction.rb @@ -1,3 +1,45 @@ module AtomicAdmin::Interaction - require_relative "interaction/atomic_application_instance_interaction" + module Manager + @@classes = [] + + def self.register(klass) + @@classes << klass + end + + def self.for(group, **kwargs) + @@key_map ||= @@classes.index_by do |klass| + klass.class_variable_get(:@@key) + end + + group_classes = @@key_map.values.select do |klass| + klass.class_variable_get(:@@group) == group && klass.class_variable_get(:@@enabled) + end.sort do |a, b| + a.class_variable_get(:@@order) <=> b.class_variable_get(:@@order) + end + + + group_classes.map do |klass| + type = klass.class_variable_get(:@@type) + hash = { + type: type, + title: klass.class_variable_get(:@@title), + icon: klass.class_variable_get(:@@icon), + key: klass.class_variable_get(:@@key), + } + + case type + when :jsonform + instance = klass.new(**kwargs) + hash[:schema] = instance.schema + hash[:uischema] = instance.uischema + end + + hash + end + end + end + + require_relative "interaction/base_interaction" + require_relative "interaction/analytics_interaction" + require_relative "interaction/general_settings_interaction" end diff --git a/lib/atomic_admin/interaction/analytics_interaction.rb b/lib/atomic_admin/interaction/analytics_interaction.rb new file mode 100644 index 0000000..37bb639 --- /dev/null +++ b/lib/atomic_admin/interaction/analytics_interaction.rb @@ -0,0 +1,8 @@ +class AtomicAdmin::Interaction::AnalyticsInteraction < AtomicAdmin::Interaction::BaseInteraction + group :application_instances + key :analytics + type :analytics + title "Analytics" + icon "bar_chart" + order 0 +end diff --git a/lib/atomic_admin/interaction/atomic_application_instance_interaction.rb b/lib/atomic_admin/interaction/atomic_application_instance_interaction.rb index 709f6cc..7deb03a 100644 --- a/lib/atomic_admin/interaction/atomic_application_instance_interaction.rb +++ b/lib/atomic_admin/interaction/atomic_application_instance_interaction.rb @@ -1,430 +1,460 @@ -module AtomicAdmin::Interaction::AtomicApplicationInstanceInteraction - def self.get - sites = Site.all +class AtomicAdmin::Interaction::AtomicApplicationInstanceInteraction + attr_reader :application_instance + + def initialize(application_instance) + @application_instance = application_instance + end + def get [ - { - type: "analytics", - title: "Overview", - icon: "bar_chart", - key: "analytics", - }, - { - type: "jsonform", - title: "General Settings", - icon: "edit_note", - key: "generalSettings", - schema: { - type: "object", - properties: { - nickname: { - type: "string", - minLength: 1, - }, - primary_contact: { - type: ["string", "null"], - }, - created_at: { - type: "string", - format: "date-time", - }, - lti_key: { - type: "string", - }, - lti_secret: { - type: "string", - }, - # The available sites is based on the sites that have been created - # so this would require it to be dynamically generated on the fly - site_id: { - type: "string", - oneOf: sites.map do |site| - { - title: site.url, - const: site.id.to_s, - } - end - }, - domain: { - type: "string", - }, - canvas_token: { - type: ["string", "null"], - }, - rollbar_enabled: { - type: "boolean", - }, - use_scoped_developer_key: { - type: "boolean", - }, + analytics_interaction, + general_settings_interaction, + configuration_interaction, + xml_configuration_interaction, + license_details_interaction, + trial_details_interaction, + ] + end + + def analytics_interaction + { + type: "analytics", + title: "Overview", + icon: "bar_chart", + key: "analytics", + } + end + + def general_settings_interaction + sites = Site.all + + { + type: "jsonform", + title: "General Settings", + icon: "edit_note", + key: "generalSettings", + schema: { + type: "object", + properties: { + nickname: { + type: "string", + minLength: 1, + }, + primary_contact: { + type: ["string", "null"], + }, + created_at: { + type: "string", + format: "date-time", + }, + lti_key: { + type: "string", + }, + lti_secret: { + type: "string", + }, + # The available sites is based on the sites that have been created + # so this would require it to be dynamically generated on the fly + site_id: { + type: "string", + oneOf: sites.map do |site| + { + title: site.url, + const: site.id.to_s, + } + end + }, + domain: { + type: "string", + }, + canvas_token: { + type: ["string", "null"], + }, + rollbar_enabled: { + type: "boolean", + }, + use_scoped_developer_key: { + type: "boolean", }, - required: ["nickname"], }, - uischema: { - type: "HorizontalLayout", - elements: [ - { - type: "Group", - label: "General Settings", - elements: [ - { - type: "VerticalLayout", - elements: [ - { - type: "Control", - scope: "#/properties/nickname", - }, - { - type: "Control", - scope: "#/properties/primary_contact", - }, - { - type: "Control", - scope: "#/properties/created_at", - label: "Date Created", - options: { - format: "date", - props: { - isReadOnly: true, - size: "small", - }, + required: ["nickname"], + }, + uischema: { + type: "HorizontalLayout", + elements: [ + { + type: "Group", + label: "General Settings", + elements: [ + { + type: "VerticalLayout", + elements: [ + { + type: "Control", + scope: "#/properties/nickname", + }, + { + type: "Control", + scope: "#/properties/primary_contact", + }, + { + type: "Control", + scope: "#/properties/created_at", + label: "Date Created", + options: { + format: "date", + props: { + isReadOnly: true, + size: "small", }, }, - { - type: "Control", - scope: "#/properties/site_id", - label: "LMS URL", - options: { - props: { - menuSize: "auto", - } + }, + { + type: "Control", + scope: "#/properties/site_id", + label: "LMS URL", + options: { + props: { + menuSize: "auto", } - }, - { - type: "Control", - scope: "#/properties/domain", - label: "LTI Tool Domain", - }, - { - type: "Control", - scope: "#/properties/canvas_token", - label: "Canvas Token", - options: { - props: { - message: "Current Canvas Token: null", - }, + } + }, + { + type: "Control", + scope: "#/properties/domain", + label: "LTI Tool Domain", + }, + { + type: "Control", + scope: "#/properties/canvas_token", + label: "Canvas Token", + options: { + props: { + message: "Current Canvas Token: null", }, }, - { - type: "Control", - scope: "#/properties/rollbar_enabled", - label: "Enable Rollbar", - }, - { - type: "Control", - scope: "#/properties/use_scoped_developer_key", - label: "Use Scoped Developer Key", - }, - ], - }, - ], - }, - { - type: "Group", - label: "LTI Key and Secret", - elements: [ - { - type: "VerticalLayout", - elements: [ - { - type: "Control", - scope: "#/properties/lti_key", - label: "LTI Key", - options: { - props: { - isReadOnly: true, - }, + }, + { + type: "Control", + scope: "#/properties/rollbar_enabled", + label: "Enable Rollbar", + }, + { + type: "Control", + scope: "#/properties/use_scoped_developer_key", + label: "Use Scoped Developer Key", + }, + ], + }, + ], + }, + { + type: "Group", + label: "LTI Key and Secret", + elements: [ + { + type: "VerticalLayout", + elements: [ + { + type: "Control", + scope: "#/properties/lti_key", + label: "LTI Key", + options: { + props: { + isReadOnly: true, }, }, - { - type: "Control", - scope: "#/properties/lti_secret", - label: "LTI Secret", - }, - ], - }, - ], - }, - ], - }, + }, + { + type: "Control", + scope: "#/properties/lti_secret", + label: "LTI Secret", + }, + ], + }, + ], + }, + ], }, - { - type: "jsonform", - title: "Configuration", - icon: "settings", - key: "configuration", - schema: { - type: "object", - properties: { - config: { - type: "object", - }, - lti_config: { - type: "object", - }, + } + end + + def configuration_interaction + { + type: "jsonform", + title: "Configuration", + icon: "settings", + key: "configuration", + schema: { + type: "object", + properties: { + config: { + type: "object", + }, + lti_config: { + type: "object", }, }, - uischema: { - type: "VerticalLayout", - elements: [ - { - type: "Group", - label: "Custom App Instance Configuration", - elements: [ - { - type: "Control", - scope: "#/properties/config", - options: { - format: "json", - props: { - size: "full", - label: "", - height: "400px", - }, + }, + uischema: { + type: "VerticalLayout", + elements: [ + { + type: "Group", + label: "Custom App Instance Configuration", + elements: [ + { + type: "Control", + scope: "#/properties/config", + options: { + format: "json", + props: { + size: "full", + label: "", + height: "400px", }, }, - ], - }, - { - type: "Group", - label: "LTI Config", - elements: [ - { - type: "Control", - scope: "#/properties/lti_config", - options: { - format: "json", - props: { - size: "full", - label: "", - height: "400px", - }, + }, + ], + }, + { + type: "Group", + label: "LTI Config", + elements: [ + { + type: "Control", + scope: "#/properties/lti_config", + options: { + format: "json", + props: { + size: "full", + label: "", + height: "400px", }, }, - ], - }, - ], - }, + }, + ], + }, + ], }, - { - type: "jsonform", - title: "XML Configuration", - icon: "code", - key: "xmlConfiguration", - schema: { - type: "object", - properties: { - lti_key: { - type: "string", - }, - lti_secret: { - type: "string", - }, - lti_config_xml: { - type: "string", - }, + } + end + + def xml_configuration_interaction + { + type: "jsonform", + title: "XML Configuration", + icon: "code", + key: "xmlConfiguration", + schema: { + type: "object", + properties: { + lti_key: { + type: "string", + }, + lti_secret: { + type: "string", + }, + lti_config_xml: { + type: "string", }, }, - uischema: { - type: "Group", - label: "XML Configuration", - elements: [ - { - type: "VerticalLayout", - elements: [ - { - type: "Control", - scope: "#/properties/lti_key", - options: { - props: { - label: "LTI Key", - isReadOnly: true, - }, + }, + uischema: { + type: "Group", + label: "XML Configuration", + elements: [ + { + type: "VerticalLayout", + elements: [ + { + type: "Control", + scope: "#/properties/lti_key", + options: { + props: { + label: "LTI Key", + isReadOnly: true, }, }, - { - type: "Control", - scope: "#/properties/lti_secret", - options: { - props: { - label: "LTI Secret", - isReadOnly: true, - }, + }, + { + type: "Control", + scope: "#/properties/lti_secret", + options: { + props: { + label: "LTI Secret", + isReadOnly: true, }, }, - { - type: "Control", - scope: "#/properties/lti_config_xml", - options: { - format: "textarea", - props: { - label: "LTI Config XML", - isReadOnly: true, - }, + }, + { + type: "Control", + scope: "#/properties/lti_config_xml", + options: { + format: "textarea", + props: { + label: "LTI Config XML", + isReadOnly: true, }, }, - ], - }, - ], - }, + }, + ], + }, + ], }, - { - type: "jsonform", - title: "License Details", - icon: "payments", - key: "licenseDetails", - schema: { - type: "object", - properties: { - license_start_date: { - type: ["string", "null"], - format: "date-time", - }, - license_end_date: { - type: ["string", "null"], - format: "date-time", - }, - licensed_users: { - type: ["number", "null"], - }, - license_type: { - type: ["string", "null"], - oneOf: [ - { - title: "Monthly", - const: "monthly", - }, - { - title: "Annual", - const: "annual", - }, - { - title: "FTE", - const: "fte", - }, - ], - }, - license_notes: { - type: ["string", "null"], - }, + } + end + + def license_details_interaction + { + type: "jsonform", + title: "License Details", + icon: "payments", + key: "licenseDetails", + schema: { + type: "object", + properties: { + license_start_date: { + type: ["string", "null"], + format: "date-time", + }, + license_end_date: { + type: ["string", "null"], + format: "date-time", + }, + licensed_users: { + type: ["number", "null"], + }, + license_type: { + type: ["string", "null"], + oneOf: [ + { + title: "Monthly", + const: "monthly", + }, + { + title: "Annual", + const: "annual", + }, + { + title: "FTE", + const: "fte", + }, + ], + }, + license_notes: { + type: ["string", "null"], }, }, - uischema: { - type: "Group", - label: "License Details", - elements: [ - { - type: "VerticalLayout", - elements: [ - { - type: "Control", - scope: "#/properties/license_start_date", - options: { - format: "date", - }, - }, - { - type: "Control", - scope: "#/properties/license_end_date", - options: { - format: "date", - }, + }, + uischema: { + type: "Group", + label: "License Details", + elements: [ + { + type: "VerticalLayout", + elements: [ + { + type: "Control", + scope: "#/properties/license_start_date", + options: { + format: "date", }, - { - type: "Control", - scope: "#/properties/licensed_users", + }, + { + type: "Control", + scope: "#/properties/license_end_date", + options: { + format: "date", }, - { - type: "Control", - scope: "#/properties/license_type", - options: { - format: "radio", - }, + }, + { + type: "Control", + scope: "#/properties/licensed_users", + }, + { + type: "Control", + scope: "#/properties/license_type", + options: { + format: "radio", }, - { - type: "Control", - scope: "#/properties/license_notes", - options: { - format: "textarea", - }, + }, + { + type: "Control", + scope: "#/properties/license_notes", + options: { + format: "textarea", }, - ], - }, - ], - }, + }, + ], + }, + ], }, - { - type: "jsonform", - title: "Trial Details", - icon: "content_paste", - key: "trialDetails", - schema: { - type: "object", - properties: { - trial_start_date: { - type: ["string", "null"], - format: "date-time", - }, - trial_end_date: { - type: ["string", "null"], - format: "date-time", - }, - trial_users: { - type: ["number", "null"], - }, - trial_notes: { - type: ["string", "null"], - }, + } + end + + def trial_details_interaction + { + type: "jsonform", + title: "Trial Details", + icon: "content_paste", + key: "trialDetails", + schema: { + type: "object", + properties: { + trial_start_date: { + type: ["string", "null"], + format: "date-time", + }, + trial_end_date: { + type: ["string", "null"], + format: "date-time", + }, + trial_users: { + type: ["number", "null"], + }, + trial_notes: { + type: ["string", "null"], }, }, - uischema: { - type: "Group", - label: "Trial Details", - elements: [ - { - type: "VerticalLayout", - elements: [ - { - type: "Control", - scope: "#/properties/trial_start_date", - options: { - format: "date", - }, - }, - { - type: "Control", - scope: "#/properties/trial_end_date", - options: { - format: "date", - }, + }, + uischema: { + type: "Group", + label: "Trial Details", + elements: [ + { + type: "VerticalLayout", + elements: [ + { + type: "Control", + scope: "#/properties/trial_start_date", + options: { + format: "date", }, - { - type: "Control", - scope: "#/properties/trial_users", + }, + { + type: "Control", + scope: "#/properties/trial_end_date", + options: { + format: "date", }, - { - type: "Control", - scope: "#/properties/trial_notes", - options: { - format: "textarea", - }, + }, + { + type: "Control", + scope: "#/properties/trial_users", + }, + { + type: "Control", + scope: "#/properties/trial_notes", + options: { + format: "textarea", }, - ], - }, - ], - }, + }, + ], + }, + ], }, - ] + } end end diff --git a/lib/atomic_admin/interaction/base_interaction.rb b/lib/atomic_admin/interaction/base_interaction.rb new file mode 100644 index 0000000..1f98748 --- /dev/null +++ b/lib/atomic_admin/interaction/base_interaction.rb @@ -0,0 +1,44 @@ +class AtomicAdmin::Interaction::BaseInteraction + def initialize(**kwargs) + kwargs.each do |key, value| + instance_variable_set("@#{key}", value) + end + end + + def self.inherited(base) + AtomicAdmin::Interaction::Manager.register(base) + base.class_variable_set(:@@enabled, true) + end + + def self.group(group) + class_variable_set(:@@group, group) + end + + def self.type(type) + class_variable_set(:@@type, type) + end + + def self.title(title) + class_variable_set(:@@title, title) + end + + def self.icon(icon) + class_variable_set(:@@icon, icon) + end + + def self.key(key) + class_variable_set(:@@key, key) + end + + def self.order(order) + class_variable_set(:@@order, order) + end + + def self.disable! + class_variable_set(:@@enabled, false) + end + + def self.enable! + class_variable_set(:@@enabled, true) + end +end diff --git a/lib/atomic_admin/interaction/general_settings_interaction.rb b/lib/atomic_admin/interaction/general_settings_interaction.rb new file mode 100644 index 0000000..8eebdc5 --- /dev/null +++ b/lib/atomic_admin/interaction/general_settings_interaction.rb @@ -0,0 +1,159 @@ +class AtomicAdmin::Interaction::GeneralSettingsInteraction < AtomicAdmin::Interaction::BaseInteraction + group :application_instances + key :general_settings + type :jsonform + title "General Settings" + icon "settings" + order 1 + + def schema + sites = Site.all + + { + type: "object", + properties: { + nickname: { + type: "string", + minLength: 1, + }, + primary_contact: { + type: ["string", "null"], + }, + created_at: { + type: "string", + format: "date-time", + }, + lti_key: { + type: "string", + }, + lti_secret: { + type: "string", + }, + # The available sites is based on the sites that have been created + # so this would require it to be dynamically generated on the fly + site_id: { + type: "string", + oneOf: sites.map do |site| + { + title: site.url, + const: site.id.to_s, + } + end + }, + domain: { + type: "string", + }, + canvas_token: { + type: ["string", "null"], + }, + rollbar_enabled: { + type: "boolean", + }, + use_scoped_developer_key: { + type: "boolean", + }, + }, + required: ["nickname"], + } + end + + def uischema + { + type: "HorizontalLayout", + elements: [ + { + type: "Group", + label: "General Settings", + elements: [ + { + type: "VerticalLayout", + elements: [ + { + type: "Control", + scope: "#/properties/nickname", + }, + { + type: "Control", + scope: "#/properties/primary_contact", + }, + { + type: "Control", + scope: "#/properties/created_at", + label: "Date Created", + options: { + format: "date", + props: { + isReadOnly: true, + size: "small", + }, + }, + }, + { + type: "Control", + scope: "#/properties/site_id", + label: "LMS URL", + options: { + props: { + menuSize: "auto", + } + } + }, + { + type: "Control", + scope: "#/properties/domain", + label: "LTI Tool Domain", + }, + { + type: "Control", + scope: "#/properties/canvas_token", + label: "Canvas Token", + options: { + props: { + message: "Current Canvas Token: null", + }, + }, + }, + { + type: "Control", + scope: "#/properties/rollbar_enabled", + label: "Enable Rollbar", + }, + { + type: "Control", + scope: "#/properties/use_scoped_developer_key", + label: "Use Scoped Developer Key", + }, + ], + }, + ], + }, + { + type: "Group", + label: "LTI Key and Secret", + elements: [ + { + type: "VerticalLayout", + elements: [ + { + type: "Control", + scope: "#/properties/lti_key", + label: "LTI Key", + options: { + props: { + isReadOnly: true, + }, + }, + }, + { + type: "Control", + scope: "#/properties/lti_secret", + label: "LTI Secret", + }, + ], + }, + ], + }, + ], + } + end +end From ea1c26175f3b72aa70bf0a6c6d624cf32df94f44 Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Thu, 2 May 2024 15:21:06 -0600 Subject: [PATCH 13/61] Second simpler pass at interactions --- ...atomic_application_instances_controller.rb | 2 +- .../atomic_applications_controller.rb | 9 +- config/routes.rb | 4 +- lib/atomic_admin.rb | 2 + lib/atomic_admin/interaction.rb | 55 ++- .../interaction/analytics_interaction.rb | 8 - ...atomic_application_instance_interaction.rb | 460 ------------------ .../interaction/base_interaction.rb | 44 -- .../general_settings_interaction.rb | 159 ------ lib/atomic_admin/schema.rb | 17 +- ...plication_instance_configuration_schema.rb | 61 +++ ...cation_instance_general_settings_schema.rb | 155 ++++++ ...ication_instance_license_details_schema.rb | 90 ++++ .../schema/application_instance_schema.rb | 18 + ...plication_instance_trial_details_schema.rb | 66 +++ .../application_instance_xml_config_schema.rb | 65 +++ .../atomic_application_update_schema.rb | 11 - 17 files changed, 500 insertions(+), 726 deletions(-) delete mode 100644 lib/atomic_admin/interaction/analytics_interaction.rb delete mode 100644 lib/atomic_admin/interaction/atomic_application_instance_interaction.rb delete mode 100644 lib/atomic_admin/interaction/base_interaction.rb delete mode 100644 lib/atomic_admin/interaction/general_settings_interaction.rb create mode 100644 lib/atomic_admin/schema/application_instance_configuration_schema.rb create mode 100644 lib/atomic_admin/schema/application_instance_general_settings_schema.rb create mode 100644 lib/atomic_admin/schema/application_instance_license_details_schema.rb create mode 100644 lib/atomic_admin/schema/application_instance_schema.rb create mode 100644 lib/atomic_admin/schema/application_instance_trial_details_schema.rb create mode 100644 lib/atomic_admin/schema/application_instance_xml_config_schema.rb diff --git a/app/controllers/atomic_admin/atomic_application_instances_controller.rb b/app/controllers/atomic_admin/atomic_application_instances_controller.rb index 48e1aa7..950cc6a 100644 --- a/app/controllers/atomic_admin/atomic_application_instances_controller.rb +++ b/app/controllers/atomic_admin/atomic_application_instances_controller.rb @@ -45,7 +45,7 @@ def show def interactions instance = ApplicationInstance.find(params[:id]) - interactions = AtomicAdmin::Interaction::Manager.for(:application_instances, application_instance: instance) + interactions = AtomicAdmin.application_instance_interactions.resolve(application_instance: instance) render json: { interactions: interactions } end diff --git a/app/controllers/atomic_admin/atomic_applications_controller.rb b/app/controllers/atomic_admin/atomic_applications_controller.rb index 2ad9a5f..980c0fd 100644 --- a/app/controllers/atomic_admin/atomic_applications_controller.rb +++ b/app/controllers/atomic_admin/atomic_applications_controller.rb @@ -25,11 +25,10 @@ def update render json: { application: json_for(@application) } end - def update_schema - render json: AtomicAdmin::Schema.for( - Application.find(params[:atomic_application_id]), - "update" - ) + def interactions + application = Application.find(params[:id]) + interactions = AtomicAdmin.application_interactions.resolve(application: application) + render json: { interactions: interactions } end private diff --git a/config/routes.rb b/config/routes.rb index f20e6c4..ba7d105 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -12,7 +12,9 @@ resources :atomic_tenant_client_id_strategy resources :atomic_applications do - get "update_schema", to: "atomic_applications#update_schema" + member do + get :interactions + end resources :atomic_application_instances do member do diff --git a/lib/atomic_admin.rb b/lib/atomic_admin.rb index 9835338..cf1c360 100644 --- a/lib/atomic_admin.rb +++ b/lib/atomic_admin.rb @@ -8,4 +8,6 @@ module AtomicAdmin mattr_accessor :admin_jwks_url mattr_accessor :audience mattr_accessor :internal_secret + mattr_accessor :application_interactions, default: AtomicAdmin::Interaction::Manager.new + mattr_accessor :application_instance_interactions, default: AtomicAdmin::Interaction::Manager.new end diff --git a/lib/atomic_admin/interaction.rb b/lib/atomic_admin/interaction.rb index 6619917..01edd96 100644 --- a/lib/atomic_admin/interaction.rb +++ b/lib/atomic_admin/interaction.rb @@ -1,45 +1,48 @@ module AtomicAdmin::Interaction - module Manager - @@classes = [] - - def self.register(klass) - @@classes << klass + class Manager + def initialize + @interactions = {} + @curr_index = 0 end - def self.for(group, **kwargs) - @@key_map ||= @@classes.index_by do |klass| - klass.class_variable_get(:@@key) - end + def add(key, **kwargs) + @interactions[key] = { + **kwargs, + order: @curr_index, + } + @curr_index += 1 + end - group_classes = @@key_map.values.select do |klass| - klass.class_variable_get(:@@group) == group && klass.class_variable_get(:@@enabled) - end.sort do |a, b| - a.class_variable_get(:@@order) <=> b.class_variable_get(:@@order) - end + def get(key) + @interactions[key] + end + def tap + yield self + self + end - group_classes.map do |klass| - type = klass.class_variable_get(:@@type) + def resolve(**kwargs) + sorted = @interactions.sort_by { |key, interaction| interaction[:order] } + sorted.map do |key, interaction| + type = interaction[:type] hash = { + key: key, type: type, - title: klass.class_variable_get(:@@title), - icon: klass.class_variable_get(:@@icon), - key: klass.class_variable_get(:@@key), + title: interaction[:title], + icon: interaction[:icon], } case type when :jsonform - instance = klass.new(**kwargs) - hash[:schema] = instance.schema - hash[:uischema] = instance.uischema + schema_factory = interaction[:schema] + schema = schema_factory.new(**kwargs) + hash[:schema] = schema.schema + hash[:uischema] = schema.uischema end hash end end end - - require_relative "interaction/base_interaction" - require_relative "interaction/analytics_interaction" - require_relative "interaction/general_settings_interaction" end diff --git a/lib/atomic_admin/interaction/analytics_interaction.rb b/lib/atomic_admin/interaction/analytics_interaction.rb deleted file mode 100644 index 37bb639..0000000 --- a/lib/atomic_admin/interaction/analytics_interaction.rb +++ /dev/null @@ -1,8 +0,0 @@ -class AtomicAdmin::Interaction::AnalyticsInteraction < AtomicAdmin::Interaction::BaseInteraction - group :application_instances - key :analytics - type :analytics - title "Analytics" - icon "bar_chart" - order 0 -end diff --git a/lib/atomic_admin/interaction/atomic_application_instance_interaction.rb b/lib/atomic_admin/interaction/atomic_application_instance_interaction.rb deleted file mode 100644 index 7deb03a..0000000 --- a/lib/atomic_admin/interaction/atomic_application_instance_interaction.rb +++ /dev/null @@ -1,460 +0,0 @@ - -class AtomicAdmin::Interaction::AtomicApplicationInstanceInteraction - attr_reader :application_instance - - def initialize(application_instance) - @application_instance = application_instance - end - - def get - [ - analytics_interaction, - general_settings_interaction, - configuration_interaction, - xml_configuration_interaction, - license_details_interaction, - trial_details_interaction, - ] - end - - def analytics_interaction - { - type: "analytics", - title: "Overview", - icon: "bar_chart", - key: "analytics", - } - end - - def general_settings_interaction - sites = Site.all - - { - type: "jsonform", - title: "General Settings", - icon: "edit_note", - key: "generalSettings", - schema: { - type: "object", - properties: { - nickname: { - type: "string", - minLength: 1, - }, - primary_contact: { - type: ["string", "null"], - }, - created_at: { - type: "string", - format: "date-time", - }, - lti_key: { - type: "string", - }, - lti_secret: { - type: "string", - }, - # The available sites is based on the sites that have been created - # so this would require it to be dynamically generated on the fly - site_id: { - type: "string", - oneOf: sites.map do |site| - { - title: site.url, - const: site.id.to_s, - } - end - }, - domain: { - type: "string", - }, - canvas_token: { - type: ["string", "null"], - }, - rollbar_enabled: { - type: "boolean", - }, - use_scoped_developer_key: { - type: "boolean", - }, - }, - required: ["nickname"], - }, - uischema: { - type: "HorizontalLayout", - elements: [ - { - type: "Group", - label: "General Settings", - elements: [ - { - type: "VerticalLayout", - elements: [ - { - type: "Control", - scope: "#/properties/nickname", - }, - { - type: "Control", - scope: "#/properties/primary_contact", - }, - { - type: "Control", - scope: "#/properties/created_at", - label: "Date Created", - options: { - format: "date", - props: { - isReadOnly: true, - size: "small", - }, - }, - }, - { - type: "Control", - scope: "#/properties/site_id", - label: "LMS URL", - options: { - props: { - menuSize: "auto", - } - } - }, - { - type: "Control", - scope: "#/properties/domain", - label: "LTI Tool Domain", - }, - { - type: "Control", - scope: "#/properties/canvas_token", - label: "Canvas Token", - options: { - props: { - message: "Current Canvas Token: null", - }, - }, - }, - { - type: "Control", - scope: "#/properties/rollbar_enabled", - label: "Enable Rollbar", - }, - { - type: "Control", - scope: "#/properties/use_scoped_developer_key", - label: "Use Scoped Developer Key", - }, - ], - }, - ], - }, - { - type: "Group", - label: "LTI Key and Secret", - elements: [ - { - type: "VerticalLayout", - elements: [ - { - type: "Control", - scope: "#/properties/lti_key", - label: "LTI Key", - options: { - props: { - isReadOnly: true, - }, - }, - }, - { - type: "Control", - scope: "#/properties/lti_secret", - label: "LTI Secret", - }, - ], - }, - ], - }, - ], - }, - } - end - - def configuration_interaction - { - type: "jsonform", - title: "Configuration", - icon: "settings", - key: "configuration", - schema: { - type: "object", - properties: { - config: { - type: "object", - }, - lti_config: { - type: "object", - }, - }, - }, - uischema: { - type: "VerticalLayout", - elements: [ - { - type: "Group", - label: "Custom App Instance Configuration", - elements: [ - { - type: "Control", - scope: "#/properties/config", - options: { - format: "json", - props: { - size: "full", - label: "", - height: "400px", - }, - }, - }, - ], - }, - { - type: "Group", - label: "LTI Config", - elements: [ - { - type: "Control", - scope: "#/properties/lti_config", - options: { - format: "json", - props: { - size: "full", - label: "", - height: "400px", - }, - }, - }, - ], - }, - ], - }, - } - end - - def xml_configuration_interaction - { - type: "jsonform", - title: "XML Configuration", - icon: "code", - key: "xmlConfiguration", - schema: { - type: "object", - properties: { - lti_key: { - type: "string", - }, - lti_secret: { - type: "string", - }, - lti_config_xml: { - type: "string", - }, - }, - }, - uischema: { - type: "Group", - label: "XML Configuration", - elements: [ - { - type: "VerticalLayout", - elements: [ - { - type: "Control", - scope: "#/properties/lti_key", - options: { - props: { - label: "LTI Key", - isReadOnly: true, - }, - }, - }, - { - type: "Control", - scope: "#/properties/lti_secret", - options: { - props: { - label: "LTI Secret", - isReadOnly: true, - }, - }, - }, - { - type: "Control", - scope: "#/properties/lti_config_xml", - options: { - format: "textarea", - props: { - label: "LTI Config XML", - isReadOnly: true, - }, - }, - }, - ], - }, - ], - }, - } - end - - def license_details_interaction - { - type: "jsonform", - title: "License Details", - icon: "payments", - key: "licenseDetails", - schema: { - type: "object", - properties: { - license_start_date: { - type: ["string", "null"], - format: "date-time", - }, - license_end_date: { - type: ["string", "null"], - format: "date-time", - }, - licensed_users: { - type: ["number", "null"], - }, - license_type: { - type: ["string", "null"], - oneOf: [ - { - title: "Monthly", - const: "monthly", - }, - { - title: "Annual", - const: "annual", - }, - { - title: "FTE", - const: "fte", - }, - ], - }, - license_notes: { - type: ["string", "null"], - }, - }, - }, - uischema: { - type: "Group", - label: "License Details", - elements: [ - { - type: "VerticalLayout", - elements: [ - { - type: "Control", - scope: "#/properties/license_start_date", - options: { - format: "date", - }, - }, - { - type: "Control", - scope: "#/properties/license_end_date", - options: { - format: "date", - }, - }, - { - type: "Control", - scope: "#/properties/licensed_users", - }, - { - type: "Control", - scope: "#/properties/license_type", - options: { - format: "radio", - }, - }, - { - type: "Control", - scope: "#/properties/license_notes", - options: { - format: "textarea", - }, - }, - ], - }, - ], - }, - } - end - - def trial_details_interaction - { - type: "jsonform", - title: "Trial Details", - icon: "content_paste", - key: "trialDetails", - schema: { - type: "object", - properties: { - trial_start_date: { - type: ["string", "null"], - format: "date-time", - }, - trial_end_date: { - type: ["string", "null"], - format: "date-time", - }, - trial_users: { - type: ["number", "null"], - }, - trial_notes: { - type: ["string", "null"], - }, - }, - }, - uischema: { - type: "Group", - label: "Trial Details", - elements: [ - { - type: "VerticalLayout", - elements: [ - { - type: "Control", - scope: "#/properties/trial_start_date", - options: { - format: "date", - }, - }, - { - type: "Control", - scope: "#/properties/trial_end_date", - options: { - format: "date", - }, - }, - { - type: "Control", - scope: "#/properties/trial_users", - }, - { - type: "Control", - scope: "#/properties/trial_notes", - options: { - format: "textarea", - }, - }, - ], - }, - ], - }, - } - end -end diff --git a/lib/atomic_admin/interaction/base_interaction.rb b/lib/atomic_admin/interaction/base_interaction.rb deleted file mode 100644 index 1f98748..0000000 --- a/lib/atomic_admin/interaction/base_interaction.rb +++ /dev/null @@ -1,44 +0,0 @@ -class AtomicAdmin::Interaction::BaseInteraction - def initialize(**kwargs) - kwargs.each do |key, value| - instance_variable_set("@#{key}", value) - end - end - - def self.inherited(base) - AtomicAdmin::Interaction::Manager.register(base) - base.class_variable_set(:@@enabled, true) - end - - def self.group(group) - class_variable_set(:@@group, group) - end - - def self.type(type) - class_variable_set(:@@type, type) - end - - def self.title(title) - class_variable_set(:@@title, title) - end - - def self.icon(icon) - class_variable_set(:@@icon, icon) - end - - def self.key(key) - class_variable_set(:@@key, key) - end - - def self.order(order) - class_variable_set(:@@order, order) - end - - def self.disable! - class_variable_set(:@@enabled, false) - end - - def self.enable! - class_variable_set(:@@enabled, true) - end -end diff --git a/lib/atomic_admin/interaction/general_settings_interaction.rb b/lib/atomic_admin/interaction/general_settings_interaction.rb deleted file mode 100644 index 8eebdc5..0000000 --- a/lib/atomic_admin/interaction/general_settings_interaction.rb +++ /dev/null @@ -1,159 +0,0 @@ -class AtomicAdmin::Interaction::GeneralSettingsInteraction < AtomicAdmin::Interaction::BaseInteraction - group :application_instances - key :general_settings - type :jsonform - title "General Settings" - icon "settings" - order 1 - - def schema - sites = Site.all - - { - type: "object", - properties: { - nickname: { - type: "string", - minLength: 1, - }, - primary_contact: { - type: ["string", "null"], - }, - created_at: { - type: "string", - format: "date-time", - }, - lti_key: { - type: "string", - }, - lti_secret: { - type: "string", - }, - # The available sites is based on the sites that have been created - # so this would require it to be dynamically generated on the fly - site_id: { - type: "string", - oneOf: sites.map do |site| - { - title: site.url, - const: site.id.to_s, - } - end - }, - domain: { - type: "string", - }, - canvas_token: { - type: ["string", "null"], - }, - rollbar_enabled: { - type: "boolean", - }, - use_scoped_developer_key: { - type: "boolean", - }, - }, - required: ["nickname"], - } - end - - def uischema - { - type: "HorizontalLayout", - elements: [ - { - type: "Group", - label: "General Settings", - elements: [ - { - type: "VerticalLayout", - elements: [ - { - type: "Control", - scope: "#/properties/nickname", - }, - { - type: "Control", - scope: "#/properties/primary_contact", - }, - { - type: "Control", - scope: "#/properties/created_at", - label: "Date Created", - options: { - format: "date", - props: { - isReadOnly: true, - size: "small", - }, - }, - }, - { - type: "Control", - scope: "#/properties/site_id", - label: "LMS URL", - options: { - props: { - menuSize: "auto", - } - } - }, - { - type: "Control", - scope: "#/properties/domain", - label: "LTI Tool Domain", - }, - { - type: "Control", - scope: "#/properties/canvas_token", - label: "Canvas Token", - options: { - props: { - message: "Current Canvas Token: null", - }, - }, - }, - { - type: "Control", - scope: "#/properties/rollbar_enabled", - label: "Enable Rollbar", - }, - { - type: "Control", - scope: "#/properties/use_scoped_developer_key", - label: "Use Scoped Developer Key", - }, - ], - }, - ], - }, - { - type: "Group", - label: "LTI Key and Secret", - elements: [ - { - type: "VerticalLayout", - elements: [ - { - type: "Control", - scope: "#/properties/lti_key", - label: "LTI Key", - options: { - props: { - isReadOnly: true, - }, - }, - }, - { - type: "Control", - scope: "#/properties/lti_secret", - label: "LTI Secret", - }, - ], - }, - ], - }, - ], - } - end -end diff --git a/lib/atomic_admin/schema.rb b/lib/atomic_admin/schema.rb index 28d24ee..d59d85c 100644 --- a/lib/atomic_admin/schema.rb +++ b/lib/atomic_admin/schema.rb @@ -1,17 +1,12 @@ - require_relative "schema/atomic_application_update_schema" +require_relative "schema/application_instance_schema" +require_relative "schema/application_instance_configuration_schema" +require_relative "schema/application_instance_general_settings_schema" +require_relative "schema/application_instance_xml_config_schema" +require_relative "schema/application_instance_trial_details_schema" +require_relative "schema/application_instance_license_details_schema" module AtomicAdmin module Schema - def self.for(resource, type) - case [resource.class.name, type] - when ["Application", "update"] - schema = AtomicApplicationUpdateSchema.for(resource) - # schema = Application.update_schema(schema) - schema - else - raise "Unknown resource type: #{resource.class.name}" - end - end end end diff --git a/lib/atomic_admin/schema/application_instance_configuration_schema.rb b/lib/atomic_admin/schema/application_instance_configuration_schema.rb new file mode 100644 index 0000000..6b3eef3 --- /dev/null +++ b/lib/atomic_admin/schema/application_instance_configuration_schema.rb @@ -0,0 +1,61 @@ +module AtomicAdmin::Schema + class ApplicationInstanceConfigurationSchema < ApplicationInstanceSchema + def schema + { + type: "object", + properties: { + config: { + type: "object", + }, + lti_config: { + type: "object", + }, + }, + } + end + + def uischema + { + type: "VerticalLayout", + elements: [ + { + type: "Group", + label: "Custom App Instance Configuration", + elements: [ + { + type: "Control", + scope: "#/properties/config", + options: { + format: "json", + props: { + size: "full", + label: "", + height: "400px", + }, + }, + }, + ], + }, + { + type: "Group", + label: "LTI Config", + elements: [ + { + type: "Control", + scope: "#/properties/lti_config", + options: { + format: "json", + props: { + size: "full", + label: "", + height: "400px", + }, + }, + }, + ], + }, + ], + } + end + end +end diff --git a/lib/atomic_admin/schema/application_instance_general_settings_schema.rb b/lib/atomic_admin/schema/application_instance_general_settings_schema.rb new file mode 100644 index 0000000..7672cea --- /dev/null +++ b/lib/atomic_admin/schema/application_instance_general_settings_schema.rb @@ -0,0 +1,155 @@ +module AtomicAdmin::Schema + class ApplicationInstanceGeneralSettingsSchema < ApplicationInstanceSchema + + def schema + sites = Site.all + + { + type: "object", + properties: { + nickname: { + type: "string", + minLength: 1, + }, + primary_contact: { + type: ["string", "null"], + }, + created_at: { + type: "string", + format: "date-time", + }, + lti_key: { + type: "string", + }, + lti_secret: { + type: "string", + }, + # The available sites is based on the sites that have been created + # so this would require it to be dynamically generated on the fly + site_id: { + type: "string", + oneOf: sites.map do |site| + { + title: site.url, + const: site.id.to_s, + } + end + }, + domain: { + type: "string", + }, + canvas_token: { + type: ["string", "null"], + }, + rollbar_enabled: { + type: "boolean", + }, + use_scoped_developer_key: { + type: "boolean", + }, + }, + required: ["nickname"], + } + end + + def uischema + { + type: "HorizontalLayout", + elements: [ + { + type: "Group", + label: "General Settings", + elements: [ + { + type: "VerticalLayout", + elements: [ + { + type: "Control", + scope: "#/properties/nickname", + }, + { + type: "Control", + scope: "#/properties/primary_contact", + }, + { + type: "Control", + scope: "#/properties/created_at", + label: "Date Created", + options: { + format: "date", + props: { + isReadOnly: true, + size: "small", + }, + }, + }, + { + type: "Control", + scope: "#/properties/site_id", + label: "LMS URL", + options: { + props: { + menuSize: "auto", + } + } + }, + { + type: "Control", + scope: "#/properties/domain", + label: "LTI Tool Domain", + }, + { + type: "Control", + scope: "#/properties/canvas_token", + label: "Canvas Token", + options: { + props: { + message: "Current Canvas Token: null", + }, + }, + }, + { + type: "Control", + scope: "#/properties/rollbar_enabled", + label: "Enable Rollbar", + }, + { + type: "Control", + scope: "#/properties/use_scoped_developer_key", + label: "Use Scoped Developer Key", + }, + ], + }, + ], + }, + { + type: "Group", + label: "LTI Key and Secret", + elements: [ + { + type: "VerticalLayout", + elements: [ + { + type: "Control", + scope: "#/properties/lti_key", + label: "LTI Key", + options: { + props: { + isReadOnly: true, + }, + }, + }, + { + type: "Control", + scope: "#/properties/lti_secret", + label: "LTI Secret", + }, + ], + }, + ], + }, + ], + } + end + end +end diff --git a/lib/atomic_admin/schema/application_instance_license_details_schema.rb b/lib/atomic_admin/schema/application_instance_license_details_schema.rb new file mode 100644 index 0000000..702950a --- /dev/null +++ b/lib/atomic_admin/schema/application_instance_license_details_schema.rb @@ -0,0 +1,90 @@ + +module AtomicAdmin::Schema + class ApplicationInstanceLicenseDetailsSchema < ApplicationInstanceSchema + + def schema + { + type: "object", + properties: { + license_start_date: { + type: ["string", "null"], + format: "date-time", + }, + license_end_date: { + type: ["string", "null"], + format: "date-time", + }, + licensed_users: { + type: ["number", "null"], + }, + license_type: { + type: ["string", "null"], + oneOf: [ + { + title: "Monthly", + const: "monthly", + }, + { + title: "Annual", + const: "annual", + }, + { + title: "FTE", + const: "fte", + }, + ], + }, + license_notes: { + type: ["string", "null"], + }, + }, + } + end + + def uischema + { + type: "Group", + label: "License Details", + elements: [ + { + type: "VerticalLayout", + elements: [ + { + type: "Control", + scope: "#/properties/license_start_date", + options: { + format: "date", + }, + }, + { + type: "Control", + scope: "#/properties/license_end_date", + options: { + format: "date", + }, + }, + { + type: "Control", + scope: "#/properties/licensed_users", + }, + { + type: "Control", + scope: "#/properties/license_type", + options: { + format: "radio", + }, + }, + { + type: "Control", + scope: "#/properties/license_notes", + options: { + format: "textarea", + }, + }, + ], + }, + ], + } + end + end +end diff --git a/lib/atomic_admin/schema/application_instance_schema.rb b/lib/atomic_admin/schema/application_instance_schema.rb new file mode 100644 index 0000000..a95388a --- /dev/null +++ b/lib/atomic_admin/schema/application_instance_schema.rb @@ -0,0 +1,18 @@ + +module AtomicAdmin::Schema + class ApplicationInstanceSchema + attr_reader :application_instance + + def initialize(application_instance:) + @application_instance = application_instance + end + + def schema + raise "Not implemented" + end + + def uischema + raise "Not implemented" + end + end +end diff --git a/lib/atomic_admin/schema/application_instance_trial_details_schema.rb b/lib/atomic_admin/schema/application_instance_trial_details_schema.rb new file mode 100644 index 0000000..fd95d5f --- /dev/null +++ b/lib/atomic_admin/schema/application_instance_trial_details_schema.rb @@ -0,0 +1,66 @@ + +module AtomicAdmin::Schema + class ApplicationInstanceTrialDetailsSchema < ApplicationInstanceSchema + + def schema + { + type: "object", + properties: { + trial_start_date: { + type: ["string", "null"], + format: "date-time", + }, + trial_end_date: { + type: ["string", "null"], + format: "date-time", + }, + trial_users: { + type: ["number", "null"], + }, + trial_notes: { + type: ["string", "null"], + }, + }, + } + end + + def uischema + { + type: "Group", + label: "License Details", + elements: [ + { + type: "VerticalLayout", + elements: [ + { + type: "Control", + scope: "#/properties/trial_start_date", + options: { + format: "date", + }, + }, + { + type: "Control", + scope: "#/properties/trial_end_date", + options: { + format: "date", + }, + }, + { + type: "Control", + scope: "#/properties/trial_users", + }, + { + type: "Control", + scope: "#/properties/trial_notes", + options: { + format: "textarea", + }, + }, + ], + }, + ], + } + end + end +end diff --git a/lib/atomic_admin/schema/application_instance_xml_config_schema.rb b/lib/atomic_admin/schema/application_instance_xml_config_schema.rb new file mode 100644 index 0000000..86b09ac --- /dev/null +++ b/lib/atomic_admin/schema/application_instance_xml_config_schema.rb @@ -0,0 +1,65 @@ +module AtomicAdmin::Schema + class ApplicationInstanceXmlConfigSchema < ApplicationInstanceSchema + def schema + { + type: "object", + properties: { + lti_key: { + type: "string", + }, + lti_secret: { + type: "string", + }, + lti_config_xml: { + type: "string", + }, + } + } + end + + def uischema + { + type: "Group", + label: "XML Configuration", + elements: [ + { + type: "VerticalLayout", + elements: [ + { + type: "Control", + scope: "#/properties/lti_key", + options: { + props: { + label: "LTI Key", + isReadOnly: true, + }, + }, + }, + { + type: "Control", + scope: "#/properties/lti_secret", + options: { + props: { + label: "LTI Secret", + isReadOnly: true, + }, + }, + }, + { + type: "Control", + scope: "#/properties/lti_config_xml", + options: { + format: "textarea", + props: { + label: "LTI Config XML", + isReadOnly: true, + }, + }, + }, + ], + }, + ], + } + end + end +end diff --git a/lib/atomic_admin/schema/atomic_application_update_schema.rb b/lib/atomic_admin/schema/atomic_application_update_schema.rb index b08783f..407b4e4 100644 --- a/lib/atomic_admin/schema/atomic_application_update_schema.rb +++ b/lib/atomic_admin/schema/atomic_application_update_schema.rb @@ -3,21 +3,10 @@ module AtomicAdmin::Schema class AtomicApplicationUpdateSchema attr_accessor :application - def self.for(application) - new(application).call - end - def initialize(application) @application = application end - def call - { - schema: schema, - uischema: uischema, - } - end - def schema { type: "object", From f296572bad91412b0af064310fd60a22b8b689fb Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Thu, 2 May 2024 16:46:01 -0600 Subject: [PATCH 14/61] Fixing validations --- .../schema/application_instance_general_settings_schema.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/atomic_admin/schema/application_instance_general_settings_schema.rb b/lib/atomic_admin/schema/application_instance_general_settings_schema.rb index 7672cea..8e18978 100644 --- a/lib/atomic_admin/schema/application_instance_general_settings_schema.rb +++ b/lib/atomic_admin/schema/application_instance_general_settings_schema.rb @@ -27,11 +27,11 @@ def schema # The available sites is based on the sites that have been created # so this would require it to be dynamically generated on the fly site_id: { - type: "string", + type: "number", oneOf: sites.map do |site| { title: site.url, - const: site.id.to_s, + const: site.id } end }, From e6f567ff1e494f4bf388bb66f8df6fde0ac84ffa Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Sun, 26 May 2024 22:44:07 -0600 Subject: [PATCH 15/61] Updating Application instances --- Gemfile.lock | 1 + ...atomic_application_instances_controller.rb | 48 +++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/Gemfile.lock b/Gemfile.lock index 17af40b..4bdaf77 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -167,6 +167,7 @@ GEM PLATFORMS arm64-darwin-22 + arm64-darwin-23 x86_64-darwin-22 DEPENDENCIES diff --git a/app/controllers/atomic_admin/atomic_application_instances_controller.rb b/app/controllers/atomic_admin/atomic_application_instances_controller.rb index 950cc6a..9a0f36f 100644 --- a/app/controllers/atomic_admin/atomic_application_instances_controller.rb +++ b/app/controllers/atomic_admin/atomic_application_instances_controller.rb @@ -43,6 +43,28 @@ def show render json: { application_instance: json_for(@application_instance) } end + def create + instance = ApplicationInstance.new(application_instance_params) + + if instance.save + render json: { application_instance: json_for(instance) } + else + render json: { errors: instance.errors }, status: 422 + end + end + + def update + instance = ApplicationInstance.find(params[:id]) + instance.update(application_instance_params) + byebug + + if instance.save + render json: { application_instance: json_for(instance) } + else + render json: { errors: instance.errors }, status: 422 + end + end + def interactions instance = ApplicationInstance.find(params[:id]) interactions = AtomicAdmin.application_instance_interactions.resolve(application_instance: instance) @@ -88,5 +110,31 @@ def type def search params[:search] end + + def application_instance_params + params.permit( + :site_id, + :lti_secret, + :lti_key, + :canvas_token, + :disabled_at, + :anonymous, + :rollbar_enabled, + :paid_at, + :domain, + :use_scoped_developer_key, + :language, + :nickname, + :primary_contact, + :trial_notes, + :trial_users, + :trial_end_date, + :trial_start_date, + :license_type, + :license_notes, + :licensed_users, + :license_end_date, + ) + end end end From 271783b6925d7bf25affc3f29946d1a359b9c84e Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Mon, 27 May 2024 17:31:03 -0600 Subject: [PATCH 16/61] Creating application instances based on schema --- ...atomic_application_instances_controller.rb | 4 +- lib/atomic_admin/schema.rb | 1 + .../application_instance_create_schema.rb | 150 ++++++++++++++++++ 3 files changed, 153 insertions(+), 2 deletions(-) create mode 100644 lib/atomic_admin/schema/application_instance_create_schema.rb diff --git a/app/controllers/atomic_admin/atomic_application_instances_controller.rb b/app/controllers/atomic_admin/atomic_application_instances_controller.rb index 9a0f36f..cbb0f6f 100644 --- a/app/controllers/atomic_admin/atomic_application_instances_controller.rb +++ b/app/controllers/atomic_admin/atomic_application_instances_controller.rb @@ -44,7 +44,8 @@ def show end def create - instance = ApplicationInstance.new(application_instance_params) + application = Application.find(params[:atomic_application_id]) + instance = application.application_instances.new(application_instance_params) if instance.save render json: { application_instance: json_for(instance) } @@ -56,7 +57,6 @@ def create def update instance = ApplicationInstance.find(params[:id]) instance.update(application_instance_params) - byebug if instance.save render json: { application_instance: json_for(instance) } diff --git a/lib/atomic_admin/schema.rb b/lib/atomic_admin/schema.rb index d59d85c..7f5b727 100644 --- a/lib/atomic_admin/schema.rb +++ b/lib/atomic_admin/schema.rb @@ -1,4 +1,5 @@ require_relative "schema/atomic_application_update_schema" +require_relative "schema/application_instance_create_schema" require_relative "schema/application_instance_schema" require_relative "schema/application_instance_configuration_schema" require_relative "schema/application_instance_general_settings_schema" diff --git a/lib/atomic_admin/schema/application_instance_create_schema.rb b/lib/atomic_admin/schema/application_instance_create_schema.rb new file mode 100644 index 0000000..41d64e2 --- /dev/null +++ b/lib/atomic_admin/schema/application_instance_create_schema.rb @@ -0,0 +1,150 @@ + +module AtomicAdmin::Schema + class ApplicationInstanceCreateSchema + attr_accessor :application + + def initialize(application) + @application = application + end + + def schema + sites = Site.all + + { + type: "object", + properties: { + nickname: { + type: "string", + minLength: 1, + }, + primary_contact: { + type: ["string", "null"], + }, + lti_key: { + type: "string", + }, + lti_secret: { + type: "string", + }, + site_id: { + type: "number", + oneOf: sites.map do |site| + { + title: site.url, + const: site.id + } + end + }, + domain: { + type: "string", + }, + canvas_token: { + type: ["string", "null"], + }, + rollbar_enabled: { + type: "boolean", + }, + use_scoped_developer_key: { + type: "boolean", + }, + paid_account: { + type: "boolean", + }, + config: { + type: "object", + }, + lti_config: { + type: "object", + }, + }, + } + end + + def uischema + { + type: "VerticalLayout", + elements: [ + { + type: "HorizontalLayout", + elements: [ + { + type: "VerticalLayout", + elements: [ + { + type: "Control", + scope: "#/properties/nickname", + }, + { + type: "Control", + scope: "#/properties/site_id", + }, + { + type: "Control", + scope: "#/properties/lti_key", + }, + { + type: "Control", + scope: "#/properties/canvas_token", + }, + ], + }, + { + type: "VerticalLayout", + elements: [ + { + type: "Control", + scope: "#/properties/primary_contact", + }, + { + type: "Control", + scope: "#/properties/domain", + }, + { + type: "Control", + scope: "#/properties/lti_secret", + }, + ], + }, + ] + }, + { + type: "Control", + scope: "#/properties/rollbar_enabled", + }, + { + type: "Control", + scope: "#/properties/use_scoped_developer_key", + }, + { + type: "Control", + scope: "#/properties/paid_account", + }, + { + type: "Control", + scope: "#/properties/config", + options: { + format: "json", + props: { + size: "full", + height: "200px", + defaultValue: "{}" + }, + }, + }, + { + type: "Control", + scope: "#/properties/lti_config", + options: { + format: "json", + props: { + size: "full", + height: "200px", + defaultValue: "{}" + }, + }, + }, + ] + } + end + end +end From 9943f7171596a307347828148b25cb6baf7bab1c Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Mon, 27 May 2024 21:23:00 -0600 Subject: [PATCH 17/61] Renamed some query params to be more clear --- .../atomic_admin/atomic_application_instances_controller.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/atomic_admin/atomic_application_instances_controller.rb b/app/controllers/atomic_admin/atomic_application_instances_controller.rb index cbb0f6f..d95e877 100644 --- a/app/controllers/atomic_admin/atomic_application_instances_controller.rb +++ b/app/controllers/atomic_admin/atomic_application_instances_controller.rb @@ -93,14 +93,14 @@ def sortable_columns end def sort_column - sortable_columns.include?(params[:column]) ? params[:column] : "created_at" + sortable_columns.include?(params[:sort_on]) ? params[:sort_on] : "created_at" end def sort_direction { "ascending" => "asc", "descending" => "desc", - }.fetch(params[:direction], "desc") + }.fetch(params[:sort_direction], "desc") end def type From bd4326ae6676834c3eb8f5f92ab5f7fd9cd65d7b Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Fri, 28 Jun 2024 18:06:38 -0600 Subject: [PATCH 18/61] Implemented sites controller --- .../atomic_admin/application_controller.rb | 17 +++++++- .../atomic_admin/atomic_sites_controller.rb | 40 +++++++++++++++++++ app/controllers/concerns/filtering.rb | 28 +++++++++++++ config/routes.rb | 3 +- .../atomic_admin/api/sites_controller_test.rb | 11 +++++ 5 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 app/controllers/atomic_admin/atomic_sites_controller.rb create mode 100644 app/controllers/concerns/filtering.rb create mode 100644 test/controllers/atomic_admin/api/sites_controller_test.rb diff --git a/app/controllers/atomic_admin/application_controller.rb b/app/controllers/atomic_admin/application_controller.rb index 1f51890..db3b723 100644 --- a/app/controllers/atomic_admin/application_controller.rb +++ b/app/controllers/atomic_admin/application_controller.rb @@ -5,11 +5,26 @@ class ApplicationController < ActionController::API rescue_from ActiveRecord::RecordNotFound, with: :record_not_found def record_not_found - render json: { message: "Record not found" }, status: 404 + render_error(:not_found) end private + def render_error(type, message: nil) + case type + when :not_found + [404, { type: "not_found", message: "Record not found" }] + else + [500, { type: "unknown", message: "An error occurred" }] + end => [status, error] + + if message.present? + error[:message] = message + end + + render json: error, status: status + end + def json_for(resource) resource.as_json end diff --git a/app/controllers/atomic_admin/atomic_sites_controller.rb b/app/controllers/atomic_admin/atomic_sites_controller.rb new file mode 100644 index 0000000..a63cc62 --- /dev/null +++ b/app/controllers/atomic_admin/atomic_sites_controller.rb @@ -0,0 +1,40 @@ +module AtomicAdmin + class AtomicSitesController < ApplicationController + include Filtering + + def index + @sites = Site.all + render json: { sites: json_for_collection(filter(@sites, search_col: "url")) } + end + + def create + @site = Site.create!(create_params) + render json: { site: json_for(@site) } + end + + def update + @site = Site.find(params[:id]) + @site.update!(update_params) + render json: { site: json_for(@site) } + end + + def destroy + @site = Site.find(params[:id]) + @site.destroy! + render json: { site: json_for(@site) } + end + + private + def json_for(site) + site.as_json(include: :leads) + end + + def create_params + params.permit(:url, :oauth_key, :oauth_secret) + end + + def update_params + params.permit(:url, :oauth_key, :oauth_secret) + end + end +end diff --git a/app/controllers/concerns/filtering.rb b/app/controllers/concerns/filtering.rb new file mode 100644 index 0000000..826250e --- /dev/null +++ b/app/controllers/concerns/filtering.rb @@ -0,0 +1,28 @@ +module Filtering + extend ActiveSupport::Concern + + def query_params + params. + permit(:search, :sort_on, :sort_direction, :page, :per_page). + with_defaults(sort_direction: "asc", page: 1, per_page: 30) + end + + def filter(relation, search_col: nil) + params = query_params + if params[:search].present? && search_col.present? + relation = relation.where("#{search_col} LIKE ?", "%#{params[:search]}%") + end + + if params[:sort_on].present? + sort_col = params[:sort_on] + sort_dir = params[:sort_direction] + sort_dir = "asc" if sort_dir == "ascending" + sort_dir = "desc" if sort_dir == "descending" + relation = relation.order(Arel.sql("#{sort_col} #{sort_dir}")) + end + + relation = relation.paginate(page: params[:page], per_page: params[:per_page]) + + relation + end +end diff --git a/config/routes.rb b/config/routes.rb index ba7d105..a4bbbc8 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,5 +1,4 @@ AtomicAdmin::Engine.routes.draw do - # namespace :lti do resources :atomic_lti_platform resources :atomic_lti_install resources :atomic_tenant_deployment @@ -11,6 +10,8 @@ resources :atomic_tenant_client_id_strategy + resources :atomic_sites + resources :atomic_applications do member do get :interactions diff --git a/test/controllers/atomic_admin/api/sites_controller_test.rb b/test/controllers/atomic_admin/api/sites_controller_test.rb new file mode 100644 index 0000000..2fb314d --- /dev/null +++ b/test/controllers/atomic_admin/api/sites_controller_test.rb @@ -0,0 +1,11 @@ +require "test_helper" + +module AtomicAdmin + class Api::SitesControllerTest < ActionDispatch::IntegrationTest + include Engine.routes.url_helpers + + # test "the truth" do + # assert true + # end + end +end From 89834bfcdef6c2c0763a73cdefc5cc83dfb49ad4 Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Wed, 24 Jul 2024 09:50:16 -0600 Subject: [PATCH 19/61] Implemented a pagination solution for resources to implement --- ...atomic_application_instances_controller.rb | 25 +++--------------- .../atomic_applications_controller.rb | 6 +++-- .../atomic_lti_platform_controller.rb | 26 +++++++++---------- .../atomic_admin/atomic_sites_controller.rb | 3 ++- app/controllers/concerns/filtering.rb | 10 ++++++- 5 files changed, 31 insertions(+), 39 deletions(-) diff --git a/app/controllers/atomic_admin/atomic_application_instances_controller.rb b/app/controllers/atomic_admin/atomic_application_instances_controller.rb index d95e877..0529335 100644 --- a/app/controllers/atomic_admin/atomic_application_instances_controller.rb +++ b/app/controllers/atomic_admin/atomic_application_instances_controller.rb @@ -1,5 +1,6 @@ module AtomicAdmin class AtomicApplicationInstancesController < ApplicationController + include Filtering def index @application_instances = ApplicationInstance.where(application_id: params[:atomic_application_id]) @@ -10,31 +11,11 @@ def index @application_instances.where(paid_at: nil) end - if search.present? - @application_instances = @application_instances. - where("nickname LIKE ?", "%#{search}%"). - or(@application_instances.where("lti_key LIKE ?", "%#{search}%")) - end - - order_by = - if sort_column === "nickname" - "LOWER(#{sort_column}) #{sort_direction}" - else - { sort_column.to_sym => sort_direction.to_sym } - end - - @application_instances = @application_instances. - order(order_by). - paginate(page: params[:page], per_page: 30) + @application_instances, meta = filter(@application_instances, search_col: "nickname") render json: { application_instances: json_for_collection(@application_instances), - meta: { - current_page: @application_instances.current_page, - next_page: @application_instances.next_page, - prev_page: @application_instances.previous_page, - total_pages: @application_instances.total_pages, - } + meta: } end diff --git a/app/controllers/atomic_admin/atomic_applications_controller.rb b/app/controllers/atomic_admin/atomic_applications_controller.rb index 980c0fd..58956e5 100644 --- a/app/controllers/atomic_admin/atomic_applications_controller.rb +++ b/app/controllers/atomic_admin/atomic_applications_controller.rb @@ -4,9 +4,11 @@ module AtomicAdmin class AtomicApplicationsController < ApplicationController + include Filtering + def index - @applications = Application.all - render json: { applications: json_for_collection(@applications.lti) } + @applications, meta = filter(Application.all.lti, search_col: "name") + render json: { applications: json_for_collection(@applications.lti), meta: } end def show diff --git a/app/controllers/atomic_admin/atomic_lti_platform_controller.rb b/app/controllers/atomic_admin/atomic_lti_platform_controller.rb index f23da1e..c5d0069 100644 --- a/app/controllers/atomic_admin/atomic_lti_platform_controller.rb +++ b/app/controllers/atomic_admin/atomic_lti_platform_controller.rb @@ -1,21 +1,11 @@ module AtomicAdmin class AtomicLtiPlatformController < ApplicationController - def platform_params - params.permit(:iss, :jwks_url, :token_url, :oidc_url) - end - - def find_platform - AtomicLti::Platform.find_by(id: params[:id]) - end + include Filtering def index - page = AtomicLti::Platform.all.order(:id).paginate(page: params[:page], per_page: 30) + platforms, meta = filter(AtomicLti::Platform.all, search_col: "iss") - render json: { - platforms: page, - page: params[:page], - total_pages: page.total_pages - } + render json: { platforms:, meta: } end def create @@ -39,5 +29,15 @@ def destroy platform.destroy render json: platform end + + private + + def platform_params + params.permit(:iss, :jwks_url, :token_url, :oidc_url) + end + + def find_platform + AtomicLti::Platform.find_by(id: params[:id]) + end end end diff --git a/app/controllers/atomic_admin/atomic_sites_controller.rb b/app/controllers/atomic_admin/atomic_sites_controller.rb index a63cc62..71fa3ab 100644 --- a/app/controllers/atomic_admin/atomic_sites_controller.rb +++ b/app/controllers/atomic_admin/atomic_sites_controller.rb @@ -4,7 +4,8 @@ class AtomicSitesController < ApplicationController def index @sites = Site.all - render json: { sites: json_for_collection(filter(@sites, search_col: "url")) } + sites, meta = filter(@sites, search_col: "url") + render json: { sites: json_for_collection(sites), meta: } end def create diff --git a/app/controllers/concerns/filtering.rb b/app/controllers/concerns/filtering.rb index 826250e..ffe56c2 100644 --- a/app/controllers/concerns/filtering.rb +++ b/app/controllers/concerns/filtering.rb @@ -23,6 +23,14 @@ def filter(relation, search_col: nil) relation = relation.paginate(page: params[:page], per_page: params[:per_page]) - relation + meta = { + current_page: relation.current_page, + next_page: relation.next_page, + prev_page: relation.previous_page, + total_pages: relation.total_pages, + total_items: relation.total_entries, + } + + [relation, meta] end end From 2c78295f9923d33a8f4d57ab1f4841afce49d822 Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Sun, 28 Jul 2024 17:45:36 -0600 Subject: [PATCH 20/61] Added Admin app support for LTI 1.3 components --- ...atomic_application_instances_controller.rb | 6 +-- ...ic_tenant_client_id_strategy_controller.rb | 47 +++++++++++-------- .../atomic_tenant_deployment_controller.rb | 47 ++++++++++++------- ...enant_platform_guid_strategy_controller.rb | 47 ++++++++++++------- config/routes.rb | 4 ++ 5 files changed, 93 insertions(+), 58 deletions(-) diff --git a/app/controllers/atomic_admin/atomic_application_instances_controller.rb b/app/controllers/atomic_admin/atomic_application_instances_controller.rb index 0529335..987faca 100644 --- a/app/controllers/atomic_admin/atomic_application_instances_controller.rb +++ b/app/controllers/atomic_admin/atomic_application_instances_controller.rb @@ -55,10 +55,8 @@ def interactions private def json_for(instance) - json = instance.as_json - json[:site] = instance.site.as_json - json[:application] = instance.application.as_json - + json = instance.as_json(include: [:site, :application]) + json.delete("canvas_token") json end diff --git a/app/controllers/atomic_admin/atomic_tenant_client_id_strategy_controller.rb b/app/controllers/atomic_admin/atomic_tenant_client_id_strategy_controller.rb index c60feaf..eae8b74 100644 --- a/app/controllers/atomic_admin/atomic_tenant_client_id_strategy_controller.rb +++ b/app/controllers/atomic_admin/atomic_tenant_client_id_strategy_controller.rb @@ -1,14 +1,8 @@ module AtomicAdmin class AtomicTenantClientIdStrategyController < ApplicationController - def pinned_client_id_params - params.permit(:iss, :client_id, :application_instance_id) - end - - def find_pinned_client_id - AtomicTenant::PinnedClientId.find_by(id: params[:id]) - end + include Filtering - def search + def search page = AtomicTenant::PinnedClientId .where(application_instance_id: params[:application_instance_id]) .order(:id).paginate(page: params[:page], per_page: 30) @@ -19,18 +13,14 @@ def search } end - # def index - # page = AtomicTenant::PinnedClientId.all.order(:id).paginate(page: params[:page], per_page: 30) - # render json: { - # pinned_client_ids: page, - # page: params[:page], - # total_pages: page.total_pages - # } - # end + def index + query = AtomicTenant::PinnedClientId.where(application_instance_id:) + page, meta = filter(query, search_col: "client_id") - def create - result = AtomicTenant::PinnedClientId.create!(pinned_client_id_params) - render json: { pinned_client_id: result } + render json: { + pinned_client_ids: page, + meta: + } end def show @@ -38,6 +28,11 @@ def show render json: {pinned_client_id: pinned_client_id} end + def create + result = AtomicTenant::PinnedClientId.create!({**pinned_client_id_params, application_instance_id:}) + render json: { pinned_client_id: result } + end + # def update # pinned_client_id = find_pinned_client_id # pinned_client_id.update!(pinned_client_id_params) @@ -50,5 +45,19 @@ def destroy pinned_client_id.destroy render json: { pinned_client_id: pinned_client_id } end + + private + + def application_instance_id + params[:application_instance_id] || params[:atomic_application_instance_id] + end + + def pinned_client_id_params + params.permit(:iss, :client_id, :application_instance_id) + end + + def find_pinned_client_id + AtomicTenant::PinnedClientId.find_by(id: params[:id]) + end end end diff --git a/app/controllers/atomic_admin/atomic_tenant_deployment_controller.rb b/app/controllers/atomic_admin/atomic_tenant_deployment_controller.rb index 0288e34..93555ee 100644 --- a/app/controllers/atomic_admin/atomic_tenant_deployment_controller.rb +++ b/app/controllers/atomic_admin/atomic_tenant_deployment_controller.rb @@ -1,12 +1,6 @@ module AtomicAdmin class AtomicTenantDeploymentController < ApplicationController - def deployment_params - params.permit(:iss, :deployment_id, :application_instance_id) - end - - def find_deployment - AtomicTenant::LtiDeployment.find_by(id: params[:id]) - end + include Filtering def search tenant_deployments = AtomicTenant::LtiDeployment @@ -16,7 +10,7 @@ def search page_ids = tenant_deployments.pluck(:iss, :deployment_id) - pairs = page_ids.reduce({}) do |acc, c| + pairs = page_ids.reduce({}) do |acc, c| iss = c[0] deployment_id = c[1] @@ -26,7 +20,7 @@ def search acc end - page = pairs.reduce([]) do |acc, pair| + page = pairs.reduce([]) do |acc, pair| iss = pair[0] deployment_ids = pair[1] @@ -41,17 +35,20 @@ def search } end - # def index - # page = AtomicTenant::LtiDeployment.all.order(:id).paginate(page: params[:page], per_page: 30) - # render json: { - # deployments: page, - # page: params[:page], - # total_pages: page.total_pages - # } - # end + def index + page, meta = filter( + AtomicTenant::LtiDeployment.where(application_instance_id:), + search_col: "deployment_id", + ) + + render json: { + deployments: page, + meta: + } + end def create - result = AtomicTenant::LtiDeployment.create!(deployment_params) + result = AtomicTenant::LtiDeployment.create!({**deployment_params, application_instance_id:}) render json: { deployment: result } end @@ -72,5 +69,19 @@ def destroy deployment.destroy render json: { deployment: deployment } end + + private + + def application_instance_id + params[:application_instance_id] || params[:atomic_application_instance_id] + end + + def deployment_params + params.permit(:iss, :deployment_id, :application_instance_id) + end + + def find_deployment + AtomicTenant::LtiDeployment.find_by(id: params[:id]) + end end end diff --git a/app/controllers/atomic_admin/atomic_tenant_platform_guid_strategy_controller.rb b/app/controllers/atomic_admin/atomic_tenant_platform_guid_strategy_controller.rb index 19cad94..7fd11b0 100644 --- a/app/controllers/atomic_admin/atomic_tenant_platform_guid_strategy_controller.rb +++ b/app/controllers/atomic_admin/atomic_tenant_platform_guid_strategy_controller.rb @@ -1,14 +1,8 @@ module AtomicAdmin class AtomicTenantPlatformGuidStrategyController < ApplicationController - def pinned_platform_guid_params - params.permit(:iss, :platform_guid, :application_id, :application_instance_id) - end - - def find_pinned_platform_guid - AtomicTenant::PinnedPlatformGuid.find(params[:id]) - end + include Filtering - def search + def search page = AtomicTenant::PinnedPlatformGuid .where(application_instance_id: params[:application_instance_id]) .order(:id).paginate(page: params[:page], per_page: 30) @@ -19,17 +13,18 @@ def search } end - # def index - # page = AtomicTenant::PinnedPlatformGuid.all.order(:id).paginate(page: params[:page], per_page: 30) - # render json: { - # pinned_platform_guids: page, - # page: params[:page], - # total_pages: page.total_pages - # } - # end + def index + query = AtomicTenant::PinnedPlatformGuid.where(application_instance_id:) + page, meta = filter(query, search_col: "platform_guid") + + render json: { + pinned_platform_guids: page, + meta: + } + end def create - result = AtomicTenant::PinnedPlatformGuid.create!(pinned_platform_guid_params) + result = AtomicTenant::PinnedPlatformGuid.create!({**pinned_platform_guid_params, application_instance_id:, application_id:}) render json: { pinned_platform_guid: result } end @@ -50,5 +45,23 @@ def destroy pinned_platform_guid.destroy render json: { pinned_platform_guid: pinned_platform_guid } end + + private + + def application_id + params[:application_id] || params[:atomic_application_id] + end + + def application_instance_id + params[:application_instance_id] || params[:atomic_application_instance_id] + end + + def pinned_platform_guid_params + params.permit(:iss, :platform_guid, :application_id, :application_instance_id) + end + + def find_pinned_platform_guid + AtomicTenant::PinnedPlatformGuid.find(params[:id]) + end end end diff --git a/config/routes.rb b/config/routes.rb index a4bbbc8..c292ebf 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -21,6 +21,10 @@ member do get :interactions end + + resources :atomic_client_id_strategies, controller: 'atomic_tenant_client_id_strategy' + resources :atomic_platform_guid_strategies, controller: 'atomic_tenant_platform_guid_strategy' + resources :atomic_deployments, controller: 'atomic_tenant_deployment' end end end From b350de80b37a819d20dac9162010ae2c1c3ff9b9 Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Sun, 28 Jul 2024 18:20:57 -0600 Subject: [PATCH 21/61] Fixed 1.1 LTI thingy --- .../schema/application_instance_xml_config_schema.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/atomic_admin/schema/application_instance_xml_config_schema.rb b/lib/atomic_admin/schema/application_instance_xml_config_schema.rb index 86b09ac..ad40f7f 100644 --- a/lib/atomic_admin/schema/application_instance_xml_config_schema.rb +++ b/lib/atomic_admin/schema/application_instance_xml_config_schema.rb @@ -53,6 +53,8 @@ def uischema props: { label: "LTI Config XML", isReadOnly: true, + size: "full", + rows: 20, }, }, }, From 8f6ae8dcde63424a45dc9ac0672eed848c811117 Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Sun, 28 Jul 2024 19:30:07 -0600 Subject: [PATCH 22/61] Added serialzer for single resource --- .../atomic_application_instances_controller.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/controllers/atomic_admin/atomic_application_instances_controller.rb b/app/controllers/atomic_admin/atomic_application_instances_controller.rb index 987faca..e21b2fd 100644 --- a/app/controllers/atomic_admin/atomic_application_instances_controller.rb +++ b/app/controllers/atomic_admin/atomic_application_instances_controller.rb @@ -54,10 +54,14 @@ def interactions private + def json_for_collection(instances) + serializer = ActiveModelSerializers::SerializableResource.new(instances.to_a) + serializer.as_json + end + def json_for(instance) - json = instance.as_json(include: [:site, :application]) - json.delete("canvas_token") - json + serializer = ActiveModelSerializers::SerializableResource.new(instance) + serializer.as_json end def sortable_columns From 177905bd4f58ecd0e4a1cf19f5781878a2e73765 Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Tue, 30 Jul 2024 15:27:55 -0600 Subject: [PATCH 23/61] Added deprecation notices --- .../atomic_admin/atomic_tenant_client_id_strategy_controller.rb | 1 + .../atomic_admin/atomic_tenant_deployment_controller.rb | 1 + .../atomic_tenant_platform_guid_strategy_controller.rb | 1 + 3 files changed, 3 insertions(+) diff --git a/app/controllers/atomic_admin/atomic_tenant_client_id_strategy_controller.rb b/app/controllers/atomic_admin/atomic_tenant_client_id_strategy_controller.rb index eae8b74..296afb3 100644 --- a/app/controllers/atomic_admin/atomic_tenant_client_id_strategy_controller.rb +++ b/app/controllers/atomic_admin/atomic_tenant_client_id_strategy_controller.rb @@ -2,6 +2,7 @@ module AtomicAdmin class AtomicTenantClientIdStrategyController < ApplicationController include Filtering + # NOTE: This endpoint is deprecated & only used by the legacy admin panel def search page = AtomicTenant::PinnedClientId .where(application_instance_id: params[:application_instance_id]) diff --git a/app/controllers/atomic_admin/atomic_tenant_deployment_controller.rb b/app/controllers/atomic_admin/atomic_tenant_deployment_controller.rb index 93555ee..cc668bc 100644 --- a/app/controllers/atomic_admin/atomic_tenant_deployment_controller.rb +++ b/app/controllers/atomic_admin/atomic_tenant_deployment_controller.rb @@ -2,6 +2,7 @@ module AtomicAdmin class AtomicTenantDeploymentController < ApplicationController include Filtering + # NOTE: This endpoint is deprecated & only used by the legacy admin panel def search tenant_deployments = AtomicTenant::LtiDeployment .where(application_instance_id: params[:application_instance_id]) diff --git a/app/controllers/atomic_admin/atomic_tenant_platform_guid_strategy_controller.rb b/app/controllers/atomic_admin/atomic_tenant_platform_guid_strategy_controller.rb index 7fd11b0..00d48ce 100644 --- a/app/controllers/atomic_admin/atomic_tenant_platform_guid_strategy_controller.rb +++ b/app/controllers/atomic_admin/atomic_tenant_platform_guid_strategy_controller.rb @@ -2,6 +2,7 @@ module AtomicAdmin class AtomicTenantPlatformGuidStrategyController < ApplicationController include Filtering + # NOTE: This endpoint is deprecated & only used by the legacy admin panel def search page = AtomicTenant::PinnedPlatformGuid .where(application_instance_id: params[:application_instance_id]) From 2a66d21eb3fa96e3422ef038263cbc94385846bc Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Tue, 13 Aug 2024 13:01:06 -0600 Subject: [PATCH 24/61] Moved to HTTParty as HTTP client --- Gemfile | 3 ++- Gemfile.lock | 9 +++++++++ lib/atomic_admin/jwt_token/jwks_decoder.rb | 3 +-- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index a82336d..4444b9f 100644 --- a/Gemfile +++ b/Gemfile @@ -12,4 +12,5 @@ gem "sprockets-rails" # gem "debug", ">= 1.0.0" gem "atomic_lti", "1.5.0" -gem 'atomic_tenant', "1.2.0" \ No newline at end of file +gem "atomic_tenant", "1.2.0" +gem "httparty" diff --git a/Gemfile.lock b/Gemfile.lock index 4bdaf77..3eb262a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -78,13 +78,19 @@ GEM atomic_tenant (1.2.0) atomic_lti (~> 1.3) rails (~> 7.0) + bigdecimal (3.1.8) builder (3.2.4) concurrent-ruby (1.2.2) crass (1.0.6) + csv (3.3.0) date (3.3.3) erubi (1.12.0) globalid (1.1.0) activesupport (>= 5.0) + httparty (0.22.0) + csv + mini_mime (>= 1.0.0) + multi_xml (>= 0.5.2) i18n (1.14.1) concurrent-ruby (~> 1.0) loofah (2.21.3) @@ -99,6 +105,8 @@ GEM method_source (1.0.0) mini_mime (1.1.5) minitest (5.19.0) + multi_xml (0.7.1) + bigdecimal (~> 3.1) net-imap (0.3.7) date net-protocol @@ -174,6 +182,7 @@ DEPENDENCIES atomic_admin! atomic_lti (= 1.5.0) atomic_tenant (= 1.2.0) + httparty sprockets-rails sqlite3 diff --git a/lib/atomic_admin/jwt_token/jwks_decoder.rb b/lib/atomic_admin/jwt_token/jwks_decoder.rb index 6ea2f15..0dd2cfa 100644 --- a/lib/atomic_admin/jwt_token/jwks_decoder.rb +++ b/lib/atomic_admin/jwt_token/jwks_decoder.rb @@ -16,8 +16,7 @@ def decode(token, validate = true) # NOTE: the cached keys only expire when we recieve a kid_not_found error keys = Rails.cache.fetch("atomic_admin_jwks") do - jwks_raw = Net::HTTP.get URI(@jwks_url) - JSON.parse(jwks_raw) + HTTParty.get(@jwks_url).parsed_response end JWT::JWK::Set.new(keys).select { |k| k[:use] == "sig" } From 59cb39109abf26f34c9b3078d4deb710ccd3d284 Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Tue, 13 Aug 2024 16:06:08 -0600 Subject: [PATCH 25/61] misc fixes --- ...atomic_application_instances_controller.rb | 1 + .../application_instance_create_schema.rb | 29 +++++++++++++++++++ .../application_instance_xml_config_schema.rb | 3 +- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/app/controllers/atomic_admin/atomic_application_instances_controller.rb b/app/controllers/atomic_admin/atomic_application_instances_controller.rb index 987faca..1a6b170 100644 --- a/app/controllers/atomic_admin/atomic_application_instances_controller.rb +++ b/app/controllers/atomic_admin/atomic_application_instances_controller.rb @@ -112,6 +112,7 @@ def application_instance_params :license_type, :license_notes, :licensed_users, + :license_start_date, :license_end_date, ) end diff --git a/lib/atomic_admin/schema/application_instance_create_schema.rb b/lib/atomic_admin/schema/application_instance_create_schema.rb index 41d64e2..47e29a7 100644 --- a/lib/atomic_admin/schema/application_instance_create_schema.rb +++ b/lib/atomic_admin/schema/application_instance_create_schema.rb @@ -12,6 +12,7 @@ def schema { type: "object", + required: ["nickname", "site_id", "lti_key", "lti_secret", "domain"], properties: { nickname: { type: "string", @@ -28,6 +29,7 @@ def schema }, site_id: { type: "number", + label: "Site URL", oneOf: sites.map do |site| { title: site.url, @@ -56,6 +58,9 @@ def schema lti_config: { type: "object", }, + anonymous: { + type: "boolean", + } }, } end @@ -107,17 +112,41 @@ def uischema }, ] }, + { + type: "Control", + scope: "#/properties/anonymous", + options: { + props: { + message: "Indicates whether or not user name and email is stored during LTI launch", + } + } + }, { type: "Control", scope: "#/properties/rollbar_enabled", + options: { + props: { + message: "Indicates whether or not rollbar is enabled for this app instance", + } + } }, { type: "Control", scope: "#/properties/use_scoped_developer_key", + options: { + props: { + message: "Indicates this is a paid or trial account", + } + } }, { type: "Control", scope: "#/properties/paid_account", + options: { + props: { + message: "Restricts the Canvas tokens generated during oauth to the minimum necessary for this application. This should only be used if the oauth key and secret are populated above and are for a scoped developer key.", + } + } }, { type: "Control", diff --git a/lib/atomic_admin/schema/application_instance_xml_config_schema.rb b/lib/atomic_admin/schema/application_instance_xml_config_schema.rb index ad40f7f..27f1e37 100644 --- a/lib/atomic_admin/schema/application_instance_xml_config_schema.rb +++ b/lib/atomic_admin/schema/application_instance_xml_config_schema.rb @@ -20,7 +20,7 @@ def schema def uischema { type: "Group", - label: "XML Configuration", + label: "LTI 1.1", elements: [ { type: "VerticalLayout", @@ -41,7 +41,6 @@ def uischema options: { props: { label: "LTI Secret", - isReadOnly: true, }, }, }, From b1643941febf286151c2f68b23dac90fbfbe369e Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Wed, 14 Aug 2024 13:51:09 -0600 Subject: [PATCH 26/61] updated filtering code --- ...atomic_application_instances_controller.rb | 5 +++- .../atomic_applications_controller.rb | 5 +++- .../atomic_lti_platform_controller.rb | 5 +++- .../atomic_admin/atomic_sites_controller.rb | 5 +++- ...ic_tenant_client_id_strategy_controller.rb | 5 +++- .../atomic_tenant_deployment_controller.rb | 8 ++--- ...enant_platform_guid_strategy_controller.rb | 5 +++- app/controllers/concerns/filtering.rb | 30 +++++++++++++++---- 8 files changed, 52 insertions(+), 16 deletions(-) diff --git a/app/controllers/atomic_admin/atomic_application_instances_controller.rb b/app/controllers/atomic_admin/atomic_application_instances_controller.rb index 1a6b170..9633042 100644 --- a/app/controllers/atomic_admin/atomic_application_instances_controller.rb +++ b/app/controllers/atomic_admin/atomic_application_instances_controller.rb @@ -2,6 +2,9 @@ module AtomicAdmin class AtomicApplicationInstancesController < ApplicationController include Filtering + allowed_sort_columns %w[nickname] + allowed_search_columns %w[nickname] + def index @application_instances = ApplicationInstance.where(application_id: params[:atomic_application_id]) @application_instances = @@ -11,7 +14,7 @@ def index @application_instances.where(paid_at: nil) end - @application_instances, meta = filter(@application_instances, search_col: "nickname") + @application_instances, meta = filter(@application_instances) render json: { application_instances: json_for_collection(@application_instances), diff --git a/app/controllers/atomic_admin/atomic_applications_controller.rb b/app/controllers/atomic_admin/atomic_applications_controller.rb index 58956e5..4026c1e 100644 --- a/app/controllers/atomic_admin/atomic_applications_controller.rb +++ b/app/controllers/atomic_admin/atomic_applications_controller.rb @@ -6,8 +6,11 @@ module AtomicAdmin class AtomicApplicationsController < ApplicationController include Filtering + allowed_sort_columns %w[name] + allowed_search_columns %w[name] + def index - @applications, meta = filter(Application.all.lti, search_col: "name") + @applications, meta = filter(Application.all.lti) render json: { applications: json_for_collection(@applications.lti), meta: } end diff --git a/app/controllers/atomic_admin/atomic_lti_platform_controller.rb b/app/controllers/atomic_admin/atomic_lti_platform_controller.rb index c5d0069..4a61a52 100644 --- a/app/controllers/atomic_admin/atomic_lti_platform_controller.rb +++ b/app/controllers/atomic_admin/atomic_lti_platform_controller.rb @@ -2,8 +2,11 @@ module AtomicAdmin class AtomicLtiPlatformController < ApplicationController include Filtering + allowed_search_columns %w[iss] + allowed_sort_columns %w[iss] + def index - platforms, meta = filter(AtomicLti::Platform.all, search_col: "iss") + platforms, meta = filter(AtomicLti::Platform.all) render json: { platforms:, meta: } end diff --git a/app/controllers/atomic_admin/atomic_sites_controller.rb b/app/controllers/atomic_admin/atomic_sites_controller.rb index 71fa3ab..c380ef6 100644 --- a/app/controllers/atomic_admin/atomic_sites_controller.rb +++ b/app/controllers/atomic_admin/atomic_sites_controller.rb @@ -2,9 +2,12 @@ module AtomicAdmin class AtomicSitesController < ApplicationController include Filtering + allowed_search_columns %w[url] + allowed_sort_columns %w[url] + def index @sites = Site.all - sites, meta = filter(@sites, search_col: "url") + sites, meta = filter(@sites) render json: { sites: json_for_collection(sites), meta: } end diff --git a/app/controllers/atomic_admin/atomic_tenant_client_id_strategy_controller.rb b/app/controllers/atomic_admin/atomic_tenant_client_id_strategy_controller.rb index 296afb3..c2abb1a 100644 --- a/app/controllers/atomic_admin/atomic_tenant_client_id_strategy_controller.rb +++ b/app/controllers/atomic_admin/atomic_tenant_client_id_strategy_controller.rb @@ -2,6 +2,9 @@ module AtomicAdmin class AtomicTenantClientIdStrategyController < ApplicationController include Filtering + allowed_search_columns %w[client_id] + allowed_sort_columns %w[client_id] + # NOTE: This endpoint is deprecated & only used by the legacy admin panel def search page = AtomicTenant::PinnedClientId @@ -16,7 +19,7 @@ def search def index query = AtomicTenant::PinnedClientId.where(application_instance_id:) - page, meta = filter(query, search_col: "client_id") + page, meta = filter(query) render json: { pinned_client_ids: page, diff --git a/app/controllers/atomic_admin/atomic_tenant_deployment_controller.rb b/app/controllers/atomic_admin/atomic_tenant_deployment_controller.rb index cc668bc..82c8467 100644 --- a/app/controllers/atomic_admin/atomic_tenant_deployment_controller.rb +++ b/app/controllers/atomic_admin/atomic_tenant_deployment_controller.rb @@ -2,6 +2,9 @@ module AtomicAdmin class AtomicTenantDeploymentController < ApplicationController include Filtering + allowed_search_columns %w[deployment_id] + allowed_sort_columns %w[deployment_id] + # NOTE: This endpoint is deprecated & only used by the legacy admin panel def search tenant_deployments = AtomicTenant::LtiDeployment @@ -37,10 +40,7 @@ def search end def index - page, meta = filter( - AtomicTenant::LtiDeployment.where(application_instance_id:), - search_col: "deployment_id", - ) + page, meta = filter(AtomicTenant::LtiDeployment.where(application_instance_id:)) render json: { deployments: page, diff --git a/app/controllers/atomic_admin/atomic_tenant_platform_guid_strategy_controller.rb b/app/controllers/atomic_admin/atomic_tenant_platform_guid_strategy_controller.rb index 00d48ce..999e7a2 100644 --- a/app/controllers/atomic_admin/atomic_tenant_platform_guid_strategy_controller.rb +++ b/app/controllers/atomic_admin/atomic_tenant_platform_guid_strategy_controller.rb @@ -2,6 +2,9 @@ module AtomicAdmin class AtomicTenantPlatformGuidStrategyController < ApplicationController include Filtering + allowed_search_columns %w[platform_guid] + allowed_sort_columns %w[platform_guid] + # NOTE: This endpoint is deprecated & only used by the legacy admin panel def search page = AtomicTenant::PinnedPlatformGuid @@ -16,7 +19,7 @@ def search def index query = AtomicTenant::PinnedPlatformGuid.where(application_instance_id:) - page, meta = filter(query, search_col: "platform_guid") + page, meta = filter(query) render json: { pinned_platform_guids: page, diff --git a/app/controllers/concerns/filtering.rb b/app/controllers/concerns/filtering.rb index ffe56c2..aab3163 100644 --- a/app/controllers/concerns/filtering.rb +++ b/app/controllers/concerns/filtering.rb @@ -1,24 +1,42 @@ module Filtering extend ActiveSupport::Concern + included do + class_variable_set(:@@allowed_sort_columns, []) + class_variable_set(:@@allowed_search_columns, []) + end + + class_methods do + def allowed_sort_columns(names) + class_variable_set(:@@allowed_sort_columns, names) + end + + def allowed_search_columns(names) + class_variable_set(:@@allowed_search_columns, names) + end + end + def query_params params. - permit(:search, :sort_on, :sort_direction, :page, :per_page). + permit(:search, :sort_on, :sort_direction, :page, :per_page, :search_on). with_defaults(sort_direction: "asc", page: 1, per_page: 30) end - def filter(relation, search_col: nil) + def filter(relation) params = query_params - if params[:search].present? && search_col.present? - relation = relation.where("#{search_col} LIKE ?", "%#{params[:search]}%") + allowed_search_columns = self.class.class_variable_get(:@@allowed_search_columns) + + if params[:search].present? && params[:search_on].present? && allowed_search_columns + relation = relation.where("lower(#{params[:search_on]}) LIKE ?", "%#{params[:search].downcase}%") end - if params[:sort_on].present? + allowed_sort_columns = self.class.class_variable_get(:@@allowed_sort_columns) + if params[:sort_on].present? && allowed_sort_columns.include?(params[:sort_on]) sort_col = params[:sort_on] sort_dir = params[:sort_direction] sort_dir = "asc" if sort_dir == "ascending" sort_dir = "desc" if sort_dir == "descending" - relation = relation.order(Arel.sql("#{sort_col} #{sort_dir}")) + relation = relation.order({sort_col => sort_dir}) end relation = relation.paginate(page: params[:page], per_page: params[:per_page]) From 3027f019f4805ba4d7996cf824ebbae97671b60b Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Fri, 23 Aug 2024 12:25:45 -0600 Subject: [PATCH 27/61] fixes --- .../atomic_admin/atomic_application_instances_controller.rb | 5 +++++ app/controllers/atomic_admin/atomic_sites_controller.rb | 3 ++- lib/atomic_admin.rb | 4 ++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/app/controllers/atomic_admin/atomic_application_instances_controller.rb b/app/controllers/atomic_admin/atomic_application_instances_controller.rb index 9633042..927e2d8 100644 --- a/app/controllers/atomic_admin/atomic_application_instances_controller.rb +++ b/app/controllers/atomic_admin/atomic_application_instances_controller.rb @@ -42,6 +42,11 @@ def update instance = ApplicationInstance.find(params[:id]) instance.update(application_instance_params) + instance.update( + config: params[:config], + lti_config: params[:lti_config], + ) + if instance.save render json: { application_instance: json_for(instance) } else diff --git a/app/controllers/atomic_admin/atomic_sites_controller.rb b/app/controllers/atomic_admin/atomic_sites_controller.rb index c380ef6..30228b1 100644 --- a/app/controllers/atomic_admin/atomic_sites_controller.rb +++ b/app/controllers/atomic_admin/atomic_sites_controller.rb @@ -29,8 +29,9 @@ def destroy end private + def json_for(site) - site.as_json(include: :leads) + site.as_json end def create_params diff --git a/lib/atomic_admin.rb b/lib/atomic_admin.rb index cf1c360..e9dfd56 100644 --- a/lib/atomic_admin.rb +++ b/lib/atomic_admin.rb @@ -10,4 +10,8 @@ module AtomicAdmin mattr_accessor :internal_secret mattr_accessor :application_interactions, default: AtomicAdmin::Interaction::Manager.new mattr_accessor :application_instance_interactions, default: AtomicAdmin::Interaction::Manager.new + + def self.configure + yield self + end end From a0f5c96656a8c71712f1191af3a07ed02b4beffe Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Fri, 23 Aug 2024 13:48:46 -0600 Subject: [PATCH 28/61] Getting registrar up and running --- .../atomic_applications_controller.rb | 2 +- ...plication_instance_general_settings_schema.rb | 3 ++- ...pplication_instance_license_details_schema.rb | 16 ++++++++++++++-- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/app/controllers/atomic_admin/atomic_applications_controller.rb b/app/controllers/atomic_admin/atomic_applications_controller.rb index 4026c1e..73d55b2 100644 --- a/app/controllers/atomic_admin/atomic_applications_controller.rb +++ b/app/controllers/atomic_admin/atomic_applications_controller.rb @@ -11,7 +11,7 @@ class AtomicApplicationsController < ApplicationController def index @applications, meta = filter(Application.all.lti) - render json: { applications: json_for_collection(@applications.lti), meta: } + render json: { applications: json_for_collection(@applications), meta: } end def show diff --git a/lib/atomic_admin/schema/application_instance_general_settings_schema.rb b/lib/atomic_admin/schema/application_instance_general_settings_schema.rb index 8e18978..6baa35a 100644 --- a/lib/atomic_admin/schema/application_instance_general_settings_schema.rb +++ b/lib/atomic_admin/schema/application_instance_general_settings_schema.rb @@ -53,6 +53,7 @@ def schema end def uischema + token_preview = @application_instance.canvas_token_preview() || "Not set" { type: "HorizontalLayout", elements: [ @@ -104,7 +105,7 @@ def uischema label: "Canvas Token", options: { props: { - message: "Current Canvas Token: null", + message: "Current Canvas Token: #{token_preview}", }, }, }, diff --git a/lib/atomic_admin/schema/application_instance_license_details_schema.rb b/lib/atomic_admin/schema/application_instance_license_details_schema.rb index 702950a..aa599fa 100644 --- a/lib/atomic_admin/schema/application_instance_license_details_schema.rb +++ b/lib/atomic_admin/schema/application_instance_license_details_schema.rb @@ -6,6 +6,11 @@ def schema { type: "object", properties: { + paid_at: { + type: ["string", "null"], + format: "date-time", + title: "Paid Account", + }, license_start_date: { type: ["string", "null"], format: "date-time", @@ -25,8 +30,8 @@ def schema const: "monthly", }, { - title: "Annual", - const: "annual", + title: "Yearly", + const: "yearly", }, { title: "FTE", @@ -49,6 +54,13 @@ def uischema { type: "VerticalLayout", elements: [ + { + type: "Control", + scope: "#/properties/paid_at", + options: { + format: "checkbox", + } + }, { type: "Control", scope: "#/properties/license_start_date", From cb0bfc9f41e481268c948d678888278760d22628 Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Tue, 5 Nov 2024 14:21:42 -0700 Subject: [PATCH 29/61] Simplified Application Instance Form Schema --- .../application_instance_create_schema.rb | 136 +++--------------- 1 file changed, 16 insertions(+), 120 deletions(-) diff --git a/lib/atomic_admin/schema/application_instance_create_schema.rb b/lib/atomic_admin/schema/application_instance_create_schema.rb index 47e29a7..243c12f 100644 --- a/lib/atomic_admin/schema/application_instance_create_schema.rb +++ b/lib/atomic_admin/schema/application_instance_create_schema.rb @@ -24,9 +24,6 @@ def schema lti_key: { type: "string", }, - lti_secret: { - type: "string", - }, site_id: { type: "number", label: "Site URL", @@ -37,30 +34,6 @@ def schema } end }, - domain: { - type: "string", - }, - canvas_token: { - type: ["string", "null"], - }, - rollbar_enabled: { - type: "boolean", - }, - use_scoped_developer_key: { - type: "boolean", - }, - paid_account: { - type: "boolean", - }, - config: { - type: "object", - }, - lti_config: { - type: "object", - }, - anonymous: { - type: "boolean", - } }, } end @@ -73,106 +46,29 @@ def uischema type: "HorizontalLayout", elements: [ { - type: "VerticalLayout", - elements: [ - { - type: "Control", - scope: "#/properties/nickname", - }, - { - type: "Control", - scope: "#/properties/site_id", - }, - { - type: "Control", - scope: "#/properties/lti_key", - }, - { - type: "Control", - scope: "#/properties/canvas_token", - }, - ], + type: "Control", + scope: "#/properties/nickname", }, { - type: "VerticalLayout", - elements: [ - { - type: "Control", - scope: "#/properties/primary_contact", - }, - { - type: "Control", - scope: "#/properties/domain", - }, - { - type: "Control", - scope: "#/properties/lti_secret", - }, - ], + type: "Control", + scope: "#/properties/primary_contact", }, - ] - }, - { - type: "Control", - scope: "#/properties/anonymous", - options: { - props: { - message: "Indicates whether or not user name and email is stored during LTI launch", - } - } - }, - { - type: "Control", - scope: "#/properties/rollbar_enabled", - options: { - props: { - message: "Indicates whether or not rollbar is enabled for this app instance", - } - } - }, - { - type: "Control", - scope: "#/properties/use_scoped_developer_key", - options: { - props: { - message: "Indicates this is a paid or trial account", - } - } + ], }, { - type: "Control", - scope: "#/properties/paid_account", - options: { - props: { - message: "Restricts the Canvas tokens generated during oauth to the minimum necessary for this application. This should only be used if the oauth key and secret are populated above and are for a scoped developer key.", - } - } - }, - { - type: "Control", - scope: "#/properties/config", - options: { - format: "json", - props: { - size: "full", - height: "200px", - defaultValue: "{}" + type: "HorizontalLayout", + elements: [ + { + type: "Control", + scope: "#/properties/site_id", }, - }, - }, - { - type: "Control", - scope: "#/properties/lti_config", - options: { - format: "json", - props: { - size: "full", - height: "200px", - defaultValue: "{}" + { + type: "Control", + scope: "#/properties/lti_key", }, - }, - }, - ] + ] + } + ] } end end From da92f2b1f6a5daf38fe84340a7561ba3b67cab3e Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Tue, 5 Nov 2024 14:21:42 -0700 Subject: [PATCH 30/61] Simplified Application Instance Form Schema --- .../application_instance_create_schema.rb | 140 +++--------------- 1 file changed, 19 insertions(+), 121 deletions(-) diff --git a/lib/atomic_admin/schema/application_instance_create_schema.rb b/lib/atomic_admin/schema/application_instance_create_schema.rb index 47e29a7..5593129 100644 --- a/lib/atomic_admin/schema/application_instance_create_schema.rb +++ b/lib/atomic_admin/schema/application_instance_create_schema.rb @@ -24,12 +24,8 @@ def schema lti_key: { type: "string", }, - lti_secret: { - type: "string", - }, site_id: { type: "number", - label: "Site URL", oneOf: sites.map do |site| { title: site.url, @@ -37,30 +33,6 @@ def schema } end }, - domain: { - type: "string", - }, - canvas_token: { - type: ["string", "null"], - }, - rollbar_enabled: { - type: "boolean", - }, - use_scoped_developer_key: { - type: "boolean", - }, - paid_account: { - type: "boolean", - }, - config: { - type: "object", - }, - lti_config: { - type: "object", - }, - anonymous: { - type: "boolean", - } }, } end @@ -73,106 +45,32 @@ def uischema type: "HorizontalLayout", elements: [ { - type: "VerticalLayout", - elements: [ - { - type: "Control", - scope: "#/properties/nickname", - }, - { - type: "Control", - scope: "#/properties/site_id", - }, - { - type: "Control", - scope: "#/properties/lti_key", - }, - { - type: "Control", - scope: "#/properties/canvas_token", - }, - ], + type: "Control", + scope: "#/properties/nickname", }, { - type: "VerticalLayout", - elements: [ - { - type: "Control", - scope: "#/properties/primary_contact", - }, - { - type: "Control", - scope: "#/properties/domain", - }, - { - type: "Control", - scope: "#/properties/lti_secret", - }, - ], + type: "Control", + scope: "#/properties/primary_contact", }, - ] - }, - { - type: "Control", - scope: "#/properties/anonymous", - options: { - props: { - message: "Indicates whether or not user name and email is stored during LTI launch", - } - } - }, - { - type: "Control", - scope: "#/properties/rollbar_enabled", - options: { - props: { - message: "Indicates whether or not rollbar is enabled for this app instance", - } - } - }, - { - type: "Control", - scope: "#/properties/use_scoped_developer_key", - options: { - props: { - message: "Indicates this is a paid or trial account", - } - } + ], }, { - type: "Control", - scope: "#/properties/paid_account", - options: { - props: { - message: "Restricts the Canvas tokens generated during oauth to the minimum necessary for this application. This should only be used if the oauth key and secret are populated above and are for a scoped developer key.", - } - } - }, - { - type: "Control", - scope: "#/properties/config", - options: { - format: "json", - props: { - size: "full", - height: "200px", - defaultValue: "{}" + type: "HorizontalLayout", + elements: [ + { + type: "Control", + scope: "#/properties/site_id", + props: { + label: "Site URL", + } }, - }, - }, - { - type: "Control", - scope: "#/properties/lti_config", - options: { - format: "json", - props: { - size: "full", - height: "200px", - defaultValue: "{}" + { + type: "Control", + scope: "#/properties/lti_key", }, - }, - }, - ] + ] + } + ] } end end From db2386cf08899f708dca5fc0a8b9168fc3cb10c8 Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Wed, 27 Nov 2024 15:56:46 -0700 Subject: [PATCH 31/61] Accept Rails 7 or Rails 8 --- atomic_admin.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/atomic_admin.gemspec b/atomic_admin.gemspec index 7701cea..1936b4c 100644 --- a/atomic_admin.gemspec +++ b/atomic_admin.gemspec @@ -18,5 +18,5 @@ Gem::Specification.new do |spec| Dir["{app,config,db,lib}/**/*", "MIT-LICENSE", "Rakefile", "README.md"] end - spec.add_dependency "rails", "~> 7.0" + spec.add_dependency "rails", ">= 7.0", "< 9.0" end From b5a8cab36326469bbefa97e66cf7843b63ad6ca1 Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Wed, 27 Nov 2024 16:38:28 -0700 Subject: [PATCH 32/61] Removed dependency on active_model_serializers --- .../atomic_application_instances_controller.rb | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/app/controllers/atomic_admin/atomic_application_instances_controller.rb b/app/controllers/atomic_admin/atomic_application_instances_controller.rb index 2dc0066..87a9cf1 100644 --- a/app/controllers/atomic_admin/atomic_application_instances_controller.rb +++ b/app/controllers/atomic_admin/atomic_application_instances_controller.rb @@ -62,16 +62,6 @@ def interactions private - def json_for_collection(instances) - serializer = ActiveModelSerializers::SerializableResource.new(instances.to_a) - serializer.as_json - end - - def json_for(instance) - serializer = ActiveModelSerializers::SerializableResource.new(instance) - serializer.as_json - end - def sortable_columns [ "created_at", From 19d45b406306ed690d0d2a1061d743a6f4439c52 Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Wed, 27 Nov 2024 17:01:10 -0700 Subject: [PATCH 33/61] App instance API fixes --- .../atomic_admin/atomic_application_instances_controller.rb | 2 +- .../schema/application_instance_create_schema.rb | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/app/controllers/atomic_admin/atomic_application_instances_controller.rb b/app/controllers/atomic_admin/atomic_application_instances_controller.rb index 87a9cf1..9e6cc9a 100644 --- a/app/controllers/atomic_admin/atomic_application_instances_controller.rb +++ b/app/controllers/atomic_admin/atomic_application_instances_controller.rb @@ -24,7 +24,7 @@ def index def show @application_instance = ApplicationInstance.find(params[:id]) - render json: { application_instance: json_for(@application_instance) } + render json: { application_instance: @application_instance.as_json(include: [:application, :site]) } end def create diff --git a/lib/atomic_admin/schema/application_instance_create_schema.rb b/lib/atomic_admin/schema/application_instance_create_schema.rb index 727084a..41e2afb 100644 --- a/lib/atomic_admin/schema/application_instance_create_schema.rb +++ b/lib/atomic_admin/schema/application_instance_create_schema.rb @@ -12,7 +12,7 @@ def schema { type: "object", - required: ["nickname", "site_id", "lti_key", "lti_secret", "domain"], + required: ["nickname", "site_id", "lti_key"], properties: { nickname: { type: "string", @@ -60,6 +60,9 @@ def uischema { type: "Control", scope: "#/properties/site_id", + props: { + label: "Site" + } }, { type: "Control", From a06928081e576fc7173db0caa7b22055b12da855 Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Tue, 3 Dec 2024 16:31:29 -0700 Subject: [PATCH 34/61] Nucleus support --- ...atomic_application_instances_controller.rb | 21 ++++++++++++- .../atomic_applications_controller.rb | 8 +++++ ...ic_tenant_client_id_strategy_controller.rb | 2 +- ...ication_instance_license_details_schema.rb | 17 +++++----- ...plication_instance_trial_details_schema.rb | 5 +-- .../atomic_application_update_schema.rb | 31 +++---------------- 6 files changed, 43 insertions(+), 41 deletions(-) diff --git a/app/controllers/atomic_admin/atomic_application_instances_controller.rb b/app/controllers/atomic_admin/atomic_application_instances_controller.rb index 9e6cc9a..a8559a8 100644 --- a/app/controllers/atomic_admin/atomic_application_instances_controller.rb +++ b/app/controllers/atomic_admin/atomic_application_instances_controller.rb @@ -24,7 +24,7 @@ def index def show @application_instance = ApplicationInstance.find(params[:id]) - render json: { application_instance: @application_instance.as_json(include: [:application, :site]) } + render json: { application_instance: json_for(@application_instance) } end def create @@ -47,6 +47,13 @@ def update lti_config: params[:lti_config], ) + if params[:is_paid] && instance.paid_at.nil? + instance.paid_at = DateTime.now + elsif params[:is_paid] == false && instance.paid_at.present? + instance.paid_at = nil + end + + if instance.save render json: { application_instance: json_for(instance) } else @@ -60,6 +67,18 @@ def interactions render json: { interactions: interactions } end + def json_for(instance) + json = instance.as_json(include: [:application, :site]) + + json["trial_start_date"] = instance.trial_start_date&.strftime("%Y-%m-%d") + json["trial_end_date"] = instance.trial_end_date&.strftime("%Y-%m-%d") + json["license_start_date"] = instance.license_start_date&.strftime("%Y-%m-%d") + json["license_end_date"] = instance.license_end_date&.strftime("%Y-%m-%d") + json["is_paid"] = instance.paid_at.present? + + json + end + private def sortable_columns diff --git a/app/controllers/atomic_admin/atomic_applications_controller.rb b/app/controllers/atomic_admin/atomic_applications_controller.rb index 73d55b2..6725c15 100644 --- a/app/controllers/atomic_admin/atomic_applications_controller.rb +++ b/app/controllers/atomic_admin/atomic_applications_controller.rb @@ -36,6 +36,14 @@ def interactions render json: { interactions: interactions } end + def json_for(application) + json = application.as_json.with_indifferent_access + secret = json[:oauth_secret] + json[:oauth_secret_preview] = secret[0..2] + '*' * (secret.length - 3) if secret + + json + end + private def application_params diff --git a/app/controllers/atomic_admin/atomic_tenant_client_id_strategy_controller.rb b/app/controllers/atomic_admin/atomic_tenant_client_id_strategy_controller.rb index c2abb1a..01306d0 100644 --- a/app/controllers/atomic_admin/atomic_tenant_client_id_strategy_controller.rb +++ b/app/controllers/atomic_admin/atomic_tenant_client_id_strategy_controller.rb @@ -41,7 +41,7 @@ def create # pinned_client_id = find_pinned_client_id # pinned_client_id.update!(pinned_client_id_params) - # render json: {pinned_client_id: find_pinned_client_id} + # render json: { pinned_client_id: find_pinned_client_id } # end def destroy diff --git a/lib/atomic_admin/schema/application_instance_license_details_schema.rb b/lib/atomic_admin/schema/application_instance_license_details_schema.rb index aa599fa..083e7e2 100644 --- a/lib/atomic_admin/schema/application_instance_license_details_schema.rb +++ b/lib/atomic_admin/schema/application_instance_license_details_schema.rb @@ -6,21 +6,21 @@ def schema { type: "object", properties: { - paid_at: { - type: ["string", "null"], - format: "date-time", - title: "Paid Account", + is_paid: { + type: "boolean", + title: "Paid Account" }, license_start_date: { type: ["string", "null"], - format: "date-time", + format: "date", }, license_end_date: { type: ["string", "null"], - format: "date-time", + format: "date", }, licensed_users: { type: ["number", "null"], + minimum: 0, }, license_type: { type: ["string", "null"], @@ -56,10 +56,7 @@ def uischema elements: [ { type: "Control", - scope: "#/properties/paid_at", - options: { - format: "checkbox", - } + scope: "#/properties/is_paid", }, { type: "Control", diff --git a/lib/atomic_admin/schema/application_instance_trial_details_schema.rb b/lib/atomic_admin/schema/application_instance_trial_details_schema.rb index fd95d5f..a428195 100644 --- a/lib/atomic_admin/schema/application_instance_trial_details_schema.rb +++ b/lib/atomic_admin/schema/application_instance_trial_details_schema.rb @@ -8,14 +8,15 @@ def schema properties: { trial_start_date: { type: ["string", "null"], - format: "date-time", + format: "date", }, trial_end_date: { type: ["string", "null"], - format: "date-time", + format: "date", }, trial_users: { type: ["number", "null"], + minimum: 0, }, trial_notes: { type: ["string", "null"], diff --git a/lib/atomic_admin/schema/atomic_application_update_schema.rb b/lib/atomic_admin/schema/atomic_application_update_schema.rb index 407b4e4..7193a6c 100644 --- a/lib/atomic_admin/schema/atomic_application_update_schema.rb +++ b/lib/atomic_admin/schema/atomic_application_update_schema.rb @@ -1,4 +1,3 @@ - module AtomicAdmin::Schema class AtomicApplicationUpdateSchema attr_accessor :application @@ -19,12 +18,10 @@ def schema }, oauth_secret: { type: ["string", "null"], - }, - default_config: { - type: "object", - }, - canvas_api_permissions: { - type: "object", + secret: { + preview: "ouath_secret_preview", + value: "oauth_secret" + } }, } } @@ -53,26 +50,6 @@ def uischema type: "Control", scope: "#/properties/oauth_secret", }, - { - type: "Control", - scope: "#/properties/default_config", - options: { - format: "json", - props: { - size: "full" - } - } - }, - { - type: "Control", - scope: "#/properties/canvas_api_permissions", - options: { - format: "json", - props: { - size: "full" - } - } - }, ] } end From c28b7c97804a9b560e706e83b2f381087a88ee69 Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Fri, 28 Mar 2025 11:38:00 -0600 Subject: [PATCH 35/61] added generator --- .../atomic_admin/install_generator.rb | 38 +++++++++++++++++++ .../atomic_admin/templates/controller.rb.erb | 3 ++ 2 files changed, 41 insertions(+) create mode 100644 lib/generators/atomic_admin/install_generator.rb create mode 100644 lib/generators/atomic_admin/templates/controller.rb.erb diff --git a/lib/generators/atomic_admin/install_generator.rb b/lib/generators/atomic_admin/install_generator.rb new file mode 100644 index 0000000..5a67edf --- /dev/null +++ b/lib/generators/atomic_admin/install_generator.rb @@ -0,0 +1,38 @@ +require "rails/generators" + +module AtomicAdmin + class InstallGenerator < Rails::Generators::Base + source_root File.expand_path('templates', __dir__) + + class_option :version, type: :string, default: 'v1', desc: 'API version (e.g. v1, v2). Defaults to v1' + + def validate_version + unless options[:version].match?(/^v\d+$/) + raise ArgumentError, 'Version must be in the format "v1", "v2", etc.' + end + end + + def create_admin_api_controllers + @version = options[:version] + + controllers = [ + 'applications', + 'application_instances', + 'lti_platforms', + 'lti_installs', + 'tenant_deployments', + 'tenant_platform_guid_strategies', + 'tenant_client_id_strategies', + 'sites' + ] + + controllers.each do |controller| + @controller = controller + template( + 'controller.rb.erb', + "app/controllers/api/admin/#{options[:version]}/#{controller}_controller.rb", + ) + end + end + end +end diff --git a/lib/generators/atomic_admin/templates/controller.rb.erb b/lib/generators/atomic_admin/templates/controller.rb.erb new file mode 100644 index 0000000..feccde0 --- /dev/null +++ b/lib/generators/atomic_admin/templates/controller.rb.erb @@ -0,0 +1,3 @@ +class Api::Admin::<%= @version.classify %>::<%= @controller.classify %>Controller < AtomicAdmin::<%= @controller.classify %>Controller + # Override required methods +end From 4d093a4b153d8adcc91f6e2c6c46e4b492e76e80 Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Fri, 28 Mar 2025 11:44:10 -0600 Subject: [PATCH 36/61] removed 'atomic' prefix from controllers --- .../{application_controller.rb => admin_controller.rb} | 2 +- ...tances_controller.rb => application_instances_controller.rb} | 2 +- ...ic_applications_controller.rb => applications_controller.rb} | 2 +- ...omic_lti_install_controller.rb => lti_install_controller.rb} | 2 +- ...ic_lti_platform_controller.rb => lti_platform_controller.rb} | 2 +- .../{atomic_sites_controller.rb => sites_controller.rb} | 2 +- ...gy_controller.rb => tenant_client_id_strategy_controller.rb} | 2 +- ...deployment_controller.rb => tenant_deployment_controller.rb} | 2 +- ...ontroller.rb => tenant_platform_guid_strategy_controller.rb} | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) rename app/controllers/atomic_admin/{application_controller.rb => admin_controller.rb} (95%) rename app/controllers/atomic_admin/{atomic_application_instances_controller.rb => application_instances_controller.rb} (98%) rename app/controllers/atomic_admin/{atomic_applications_controller.rb => applications_controller.rb} (96%) rename app/controllers/atomic_admin/{atomic_lti_install_controller.rb => lti_install_controller.rb} (91%) rename app/controllers/atomic_admin/{atomic_lti_platform_controller.rb => lti_platform_controller.rb} (93%) rename app/controllers/atomic_admin/{atomic_sites_controller.rb => sites_controller.rb} (94%) rename app/controllers/atomic_admin/{atomic_tenant_client_id_strategy_controller.rb => tenant_client_id_strategy_controller.rb} (96%) rename app/controllers/atomic_admin/{atomic_tenant_deployment_controller.rb => tenant_deployment_controller.rb} (97%) rename app/controllers/atomic_admin/{atomic_tenant_platform_guid_strategy_controller.rb => tenant_platform_guid_strategy_controller.rb} (96%) diff --git a/app/controllers/atomic_admin/application_controller.rb b/app/controllers/atomic_admin/admin_controller.rb similarity index 95% rename from app/controllers/atomic_admin/application_controller.rb rename to app/controllers/atomic_admin/admin_controller.rb index db3b723..4d5a997 100644 --- a/app/controllers/atomic_admin/application_controller.rb +++ b/app/controllers/atomic_admin/admin_controller.rb @@ -1,5 +1,5 @@ module AtomicAdmin - class ApplicationController < ActionController::API + class AdminController < ActionController::API include RequireJwtToken before_action :only_admins! diff --git a/app/controllers/atomic_admin/atomic_application_instances_controller.rb b/app/controllers/atomic_admin/application_instances_controller.rb similarity index 98% rename from app/controllers/atomic_admin/atomic_application_instances_controller.rb rename to app/controllers/atomic_admin/application_instances_controller.rb index a8559a8..c7daca7 100644 --- a/app/controllers/atomic_admin/atomic_application_instances_controller.rb +++ b/app/controllers/atomic_admin/application_instances_controller.rb @@ -1,5 +1,5 @@ module AtomicAdmin - class AtomicApplicationInstancesController < ApplicationController + class ApplicationInstancesController < AdminController include Filtering allowed_sort_columns %w[nickname] diff --git a/app/controllers/atomic_admin/atomic_applications_controller.rb b/app/controllers/atomic_admin/applications_controller.rb similarity index 96% rename from app/controllers/atomic_admin/atomic_applications_controller.rb rename to app/controllers/atomic_admin/applications_controller.rb index 6725c15..35bb4c7 100644 --- a/app/controllers/atomic_admin/atomic_applications_controller.rb +++ b/app/controllers/atomic_admin/applications_controller.rb @@ -3,7 +3,7 @@ # 2. The controllers call into the resource's class with the payloads and it handles the details of the implementation module AtomicAdmin - class AtomicApplicationsController < ApplicationController + class ApplicationsController < AdminController include Filtering allowed_sort_columns %w[name] diff --git a/app/controllers/atomic_admin/atomic_lti_install_controller.rb b/app/controllers/atomic_admin/lti_install_controller.rb similarity index 91% rename from app/controllers/atomic_admin/atomic_lti_install_controller.rb rename to app/controllers/atomic_admin/lti_install_controller.rb index d17e8d0..285d3be 100644 --- a/app/controllers/atomic_admin/atomic_lti_install_controller.rb +++ b/app/controllers/atomic_admin/lti_install_controller.rb @@ -1,5 +1,5 @@ module AtomicAdmin - class AtomicLtiInstallController < ApplicationController + class LtiInstallController < AdminController def install_params params.permit(:iss, :client_id) end diff --git a/app/controllers/atomic_admin/atomic_lti_platform_controller.rb b/app/controllers/atomic_admin/lti_platform_controller.rb similarity index 93% rename from app/controllers/atomic_admin/atomic_lti_platform_controller.rb rename to app/controllers/atomic_admin/lti_platform_controller.rb index 4a61a52..51dd88b 100644 --- a/app/controllers/atomic_admin/atomic_lti_platform_controller.rb +++ b/app/controllers/atomic_admin/lti_platform_controller.rb @@ -1,5 +1,5 @@ module AtomicAdmin - class AtomicLtiPlatformController < ApplicationController + class LtiPlatformController < AdminController include Filtering allowed_search_columns %w[iss] diff --git a/app/controllers/atomic_admin/atomic_sites_controller.rb b/app/controllers/atomic_admin/sites_controller.rb similarity index 94% rename from app/controllers/atomic_admin/atomic_sites_controller.rb rename to app/controllers/atomic_admin/sites_controller.rb index 30228b1..6675439 100644 --- a/app/controllers/atomic_admin/atomic_sites_controller.rb +++ b/app/controllers/atomic_admin/sites_controller.rb @@ -1,5 +1,5 @@ module AtomicAdmin - class AtomicSitesController < ApplicationController + class SitesController < AdminController include Filtering allowed_search_columns %w[url] diff --git a/app/controllers/atomic_admin/atomic_tenant_client_id_strategy_controller.rb b/app/controllers/atomic_admin/tenant_client_id_strategy_controller.rb similarity index 96% rename from app/controllers/atomic_admin/atomic_tenant_client_id_strategy_controller.rb rename to app/controllers/atomic_admin/tenant_client_id_strategy_controller.rb index 01306d0..15d9214 100644 --- a/app/controllers/atomic_admin/atomic_tenant_client_id_strategy_controller.rb +++ b/app/controllers/atomic_admin/tenant_client_id_strategy_controller.rb @@ -1,5 +1,5 @@ module AtomicAdmin - class AtomicTenantClientIdStrategyController < ApplicationController + class TenantClientIdStrategyController < AdminController include Filtering allowed_search_columns %w[client_id] diff --git a/app/controllers/atomic_admin/atomic_tenant_deployment_controller.rb b/app/controllers/atomic_admin/tenant_deployment_controller.rb similarity index 97% rename from app/controllers/atomic_admin/atomic_tenant_deployment_controller.rb rename to app/controllers/atomic_admin/tenant_deployment_controller.rb index 43bf2d1..57310ba 100644 --- a/app/controllers/atomic_admin/atomic_tenant_deployment_controller.rb +++ b/app/controllers/atomic_admin/tenant_deployment_controller.rb @@ -1,5 +1,5 @@ module AtomicAdmin - class AtomicTenantDeploymentController < ApplicationController + class TenantDeploymentController < AdminController include Filtering allowed_search_columns %w[deployment_id] diff --git a/app/controllers/atomic_admin/atomic_tenant_platform_guid_strategy_controller.rb b/app/controllers/atomic_admin/tenant_platform_guid_strategy_controller.rb similarity index 96% rename from app/controllers/atomic_admin/atomic_tenant_platform_guid_strategy_controller.rb rename to app/controllers/atomic_admin/tenant_platform_guid_strategy_controller.rb index 999e7a2..e495ffb 100644 --- a/app/controllers/atomic_admin/atomic_tenant_platform_guid_strategy_controller.rb +++ b/app/controllers/atomic_admin/tenant_platform_guid_strategy_controller.rb @@ -1,5 +1,5 @@ module AtomicAdmin - class AtomicTenantPlatformGuidStrategyController < ApplicationController + class TenantPlatformGuidStrategyController < AdminController include Filtering allowed_search_columns %w[platform_guid] From 0cd61662675df0544995cbca587e0a8d120f6887 Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Fri, 28 Mar 2025 13:23:42 -0600 Subject: [PATCH 37/61] moved controllers and aliased them to their correct path --- .../v1/application_instances_controller.rb | 3 ++ .../api/admin/v1/applications_controller.rb | 3 ++ .../api/admin/v1/lti_installs_controller.rb | 3 ++ .../api/admin/v1/lti_platforms_controller.rb | 3 ++ .../api/admin/v1/sites_controller.rb | 3 ++ .../tenant_client_id_strategies_controller.rb | 3 ++ .../admin/v1/tenant_deployments_controller.rb | 3 ++ ...ant_platform_guid_strategies_controller.rb | 3 ++ .../atomic_admin/{ => v1}/admin_controller.rb | 2 +- .../application_instances_controller.rb | 6 +-- .../{ => v1}/applications_controller.rb | 6 +-- .../lti_installs_controller.rb} | 4 +- .../lti_platforms_controller.rb} | 4 +- .../atomic_admin/{ => v1}/sites_controller.rb | 2 +- ...tenant_client_id_strategies_controller.rb} | 6 +-- .../tenant_deployments_controller.rb} | 6 +-- ...nt_platform_guid_strategies_controller.rb} | 8 ++-- config/routes.rb | 42 +++++++++---------- .../atomic_admin/templates/controller.rb.erb | 2 +- 19 files changed, 65 insertions(+), 47 deletions(-) create mode 100644 app/controllers/atomic_admin/api/admin/v1/application_instances_controller.rb create mode 100644 app/controllers/atomic_admin/api/admin/v1/applications_controller.rb create mode 100644 app/controllers/atomic_admin/api/admin/v1/lti_installs_controller.rb create mode 100644 app/controllers/atomic_admin/api/admin/v1/lti_platforms_controller.rb create mode 100644 app/controllers/atomic_admin/api/admin/v1/sites_controller.rb create mode 100644 app/controllers/atomic_admin/api/admin/v1/tenant_client_id_strategies_controller.rb create mode 100644 app/controllers/atomic_admin/api/admin/v1/tenant_deployments_controller.rb create mode 100644 app/controllers/atomic_admin/api/admin/v1/tenant_platform_guid_strategies_controller.rb rename app/controllers/atomic_admin/{ => v1}/admin_controller.rb (97%) rename app/controllers/atomic_admin/{ => v1}/application_instances_controller.rb (96%) rename app/controllers/atomic_admin/{ => v1}/applications_controller.rb (84%) rename app/controllers/atomic_admin/{lti_install_controller.rb => v1/lti_installs_controller.rb} (90%) rename app/controllers/atomic_admin/{lti_platform_controller.rb => v1/lti_platforms_controller.rb} (92%) rename app/controllers/atomic_admin/{ => v1}/sites_controller.rb (97%) rename app/controllers/atomic_admin/{tenant_client_id_strategy_controller.rb => v1/tenant_client_id_strategies_controller.rb} (91%) rename app/controllers/atomic_admin/{tenant_deployment_controller.rb => v1/tenant_deployments_controller.rb} (93%) rename app/controllers/atomic_admin/{tenant_platform_guid_strategy_controller.rb => v1/tenant_platform_guid_strategies_controller.rb} (88%) diff --git a/app/controllers/atomic_admin/api/admin/v1/application_instances_controller.rb b/app/controllers/atomic_admin/api/admin/v1/application_instances_controller.rb new file mode 100644 index 0000000..79a8669 --- /dev/null +++ b/app/controllers/atomic_admin/api/admin/v1/application_instances_controller.rb @@ -0,0 +1,3 @@ +module AtomicAdmin::Api::Admin::V1 + ApplicationInstancesController = AtomicAdmin::V1::ApplicationInstancesController +end diff --git a/app/controllers/atomic_admin/api/admin/v1/applications_controller.rb b/app/controllers/atomic_admin/api/admin/v1/applications_controller.rb new file mode 100644 index 0000000..3df539d --- /dev/null +++ b/app/controllers/atomic_admin/api/admin/v1/applications_controller.rb @@ -0,0 +1,3 @@ +module AtomicAdmin::Api::Admin::V1 + ApplicationsController = AtomicAdmin::V1::ApplicationsController +end diff --git a/app/controllers/atomic_admin/api/admin/v1/lti_installs_controller.rb b/app/controllers/atomic_admin/api/admin/v1/lti_installs_controller.rb new file mode 100644 index 0000000..767dfcb --- /dev/null +++ b/app/controllers/atomic_admin/api/admin/v1/lti_installs_controller.rb @@ -0,0 +1,3 @@ +module AtomicAdmin::Api::Admin::V1 + LtiInstallsController = AtomicAdmin::V1::LtiInstallsController +end diff --git a/app/controllers/atomic_admin/api/admin/v1/lti_platforms_controller.rb b/app/controllers/atomic_admin/api/admin/v1/lti_platforms_controller.rb new file mode 100644 index 0000000..0e59276 --- /dev/null +++ b/app/controllers/atomic_admin/api/admin/v1/lti_platforms_controller.rb @@ -0,0 +1,3 @@ +module AtomicAdmin::Api::Admin::V1 + LtiPlatformsController = AtomicAdmin::V1::LtiPlatformsController +end diff --git a/app/controllers/atomic_admin/api/admin/v1/sites_controller.rb b/app/controllers/atomic_admin/api/admin/v1/sites_controller.rb new file mode 100644 index 0000000..42c0c69 --- /dev/null +++ b/app/controllers/atomic_admin/api/admin/v1/sites_controller.rb @@ -0,0 +1,3 @@ +module AtomicAdmin::Api::Admin::V1 + SitesController = AtomicAdmin::V1::SitesController +end diff --git a/app/controllers/atomic_admin/api/admin/v1/tenant_client_id_strategies_controller.rb b/app/controllers/atomic_admin/api/admin/v1/tenant_client_id_strategies_controller.rb new file mode 100644 index 0000000..8bd9b3e --- /dev/null +++ b/app/controllers/atomic_admin/api/admin/v1/tenant_client_id_strategies_controller.rb @@ -0,0 +1,3 @@ +module AtomicAdmin::Api::Admin::V1 + TenantClientIdStrategiesController = AtomicAdmin::V1::TenantClientIdStrategiesController +end diff --git a/app/controllers/atomic_admin/api/admin/v1/tenant_deployments_controller.rb b/app/controllers/atomic_admin/api/admin/v1/tenant_deployments_controller.rb new file mode 100644 index 0000000..7661a03 --- /dev/null +++ b/app/controllers/atomic_admin/api/admin/v1/tenant_deployments_controller.rb @@ -0,0 +1,3 @@ +module AtomicAdmin::Api::Admin::V1 + TenantDeploymentsController = AtomicAdmin::V1::TenantDeploymentsController +end diff --git a/app/controllers/atomic_admin/api/admin/v1/tenant_platform_guid_strategies_controller.rb b/app/controllers/atomic_admin/api/admin/v1/tenant_platform_guid_strategies_controller.rb new file mode 100644 index 0000000..3cf15fc --- /dev/null +++ b/app/controllers/atomic_admin/api/admin/v1/tenant_platform_guid_strategies_controller.rb @@ -0,0 +1,3 @@ +module AtomicAdmin::Api::Admin::V1 + TenantPlatformGuidStrategiesController = AtomicAdmin::V1::TenantPlatformGuidStrategiesController +end diff --git a/app/controllers/atomic_admin/admin_controller.rb b/app/controllers/atomic_admin/v1/admin_controller.rb similarity index 97% rename from app/controllers/atomic_admin/admin_controller.rb rename to app/controllers/atomic_admin/v1/admin_controller.rb index 4d5a997..519dbdd 100644 --- a/app/controllers/atomic_admin/admin_controller.rb +++ b/app/controllers/atomic_admin/v1/admin_controller.rb @@ -1,4 +1,4 @@ -module AtomicAdmin +module AtomicAdmin::V1 class AdminController < ActionController::API include RequireJwtToken before_action :only_admins! diff --git a/app/controllers/atomic_admin/application_instances_controller.rb b/app/controllers/atomic_admin/v1/application_instances_controller.rb similarity index 96% rename from app/controllers/atomic_admin/application_instances_controller.rb rename to app/controllers/atomic_admin/v1/application_instances_controller.rb index c7daca7..8d7d3ed 100644 --- a/app/controllers/atomic_admin/application_instances_controller.rb +++ b/app/controllers/atomic_admin/v1/application_instances_controller.rb @@ -1,4 +1,4 @@ -module AtomicAdmin +module AtomicAdmin::V1 class ApplicationInstancesController < AdminController include Filtering @@ -6,7 +6,7 @@ class ApplicationInstancesController < AdminController allowed_search_columns %w[nickname] def index - @application_instances = ApplicationInstance.where(application_id: params[:atomic_application_id]) + @application_instances = ApplicationInstance.where(application_id: params[:application_id]) @application_instances = if type == "paid" @application_instances.where.not(paid_at: nil) @@ -28,7 +28,7 @@ def show end def create - application = Application.find(params[:atomic_application_id]) + application = Application.find(params[:application_id]) instance = application.application_instances.new(application_instance_params) if instance.save diff --git a/app/controllers/atomic_admin/applications_controller.rb b/app/controllers/atomic_admin/v1/applications_controller.rb similarity index 84% rename from app/controllers/atomic_admin/applications_controller.rb rename to app/controllers/atomic_admin/v1/applications_controller.rb index 35bb4c7..3facb4f 100644 --- a/app/controllers/atomic_admin/applications_controller.rb +++ b/app/controllers/atomic_admin/v1/applications_controller.rb @@ -1,8 +1,4 @@ -# How do we handle customization of these endpoints? -# 1. The tools override the controller and add the custom logic -# 2. The controllers call into the resource's class with the payloads and it handles the details of the implementation - -module AtomicAdmin +module AtomicAdmin::V1 class ApplicationsController < AdminController include Filtering diff --git a/app/controllers/atomic_admin/lti_install_controller.rb b/app/controllers/atomic_admin/v1/lti_installs_controller.rb similarity index 90% rename from app/controllers/atomic_admin/lti_install_controller.rb rename to app/controllers/atomic_admin/v1/lti_installs_controller.rb index 285d3be..bc97ef5 100644 --- a/app/controllers/atomic_admin/lti_install_controller.rb +++ b/app/controllers/atomic_admin/v1/lti_installs_controller.rb @@ -1,5 +1,5 @@ -module AtomicAdmin - class LtiInstallController < AdminController +module AtomicAdmin::V1 + class LtiInstallsController < AdminController def install_params params.permit(:iss, :client_id) end diff --git a/app/controllers/atomic_admin/lti_platform_controller.rb b/app/controllers/atomic_admin/v1/lti_platforms_controller.rb similarity index 92% rename from app/controllers/atomic_admin/lti_platform_controller.rb rename to app/controllers/atomic_admin/v1/lti_platforms_controller.rb index 51dd88b..fe6df53 100644 --- a/app/controllers/atomic_admin/lti_platform_controller.rb +++ b/app/controllers/atomic_admin/v1/lti_platforms_controller.rb @@ -1,5 +1,5 @@ -module AtomicAdmin - class LtiPlatformController < AdminController +module AtomicAdmin::V1 + class LtiPlatformsController < AdminController include Filtering allowed_search_columns %w[iss] diff --git a/app/controllers/atomic_admin/sites_controller.rb b/app/controllers/atomic_admin/v1/sites_controller.rb similarity index 97% rename from app/controllers/atomic_admin/sites_controller.rb rename to app/controllers/atomic_admin/v1/sites_controller.rb index 6675439..65111f1 100644 --- a/app/controllers/atomic_admin/sites_controller.rb +++ b/app/controllers/atomic_admin/v1/sites_controller.rb @@ -1,4 +1,4 @@ -module AtomicAdmin +module AtomicAdmin::V1 class SitesController < AdminController include Filtering diff --git a/app/controllers/atomic_admin/tenant_client_id_strategy_controller.rb b/app/controllers/atomic_admin/v1/tenant_client_id_strategies_controller.rb similarity index 91% rename from app/controllers/atomic_admin/tenant_client_id_strategy_controller.rb rename to app/controllers/atomic_admin/v1/tenant_client_id_strategies_controller.rb index 15d9214..6cdc797 100644 --- a/app/controllers/atomic_admin/tenant_client_id_strategy_controller.rb +++ b/app/controllers/atomic_admin/v1/tenant_client_id_strategies_controller.rb @@ -1,5 +1,5 @@ -module AtomicAdmin - class TenantClientIdStrategyController < AdminController +module AtomicAdmin::V1 + class TenantClientIdStrategiesController < AdminController include Filtering allowed_search_columns %w[client_id] @@ -53,7 +53,7 @@ def destroy private def application_instance_id - params[:application_instance_id] || params[:atomic_application_instance_id] + params[:application_instance_id] || params[:application_instance_id] end def pinned_client_id_params diff --git a/app/controllers/atomic_admin/tenant_deployment_controller.rb b/app/controllers/atomic_admin/v1/tenant_deployments_controller.rb similarity index 93% rename from app/controllers/atomic_admin/tenant_deployment_controller.rb rename to app/controllers/atomic_admin/v1/tenant_deployments_controller.rb index 57310ba..d182bdf 100644 --- a/app/controllers/atomic_admin/tenant_deployment_controller.rb +++ b/app/controllers/atomic_admin/v1/tenant_deployments_controller.rb @@ -1,5 +1,5 @@ -module AtomicAdmin - class TenantDeploymentController < AdminController +module AtomicAdmin::V1 + class TenantDeploymentsController < AdminController include Filtering allowed_search_columns %w[deployment_id] @@ -77,7 +77,7 @@ def destroy private def application_instance_id - params[:application_instance_id] || params[:atomic_application_instance_id] + params[:application_instance_id] || params[:application_instance_id] end def deployment_params diff --git a/app/controllers/atomic_admin/tenant_platform_guid_strategy_controller.rb b/app/controllers/atomic_admin/v1/tenant_platform_guid_strategies_controller.rb similarity index 88% rename from app/controllers/atomic_admin/tenant_platform_guid_strategy_controller.rb rename to app/controllers/atomic_admin/v1/tenant_platform_guid_strategies_controller.rb index e495ffb..6515320 100644 --- a/app/controllers/atomic_admin/tenant_platform_guid_strategy_controller.rb +++ b/app/controllers/atomic_admin/v1/tenant_platform_guid_strategies_controller.rb @@ -1,5 +1,5 @@ -module AtomicAdmin - class TenantPlatformGuidStrategyController < AdminController +module AtomicAdmin::V1 + class TenantPlatformGuidStrategiesController < AdminController include Filtering allowed_search_columns %w[platform_guid] @@ -53,11 +53,11 @@ def destroy private def application_id - params[:application_id] || params[:atomic_application_id] + params[:application_id] end def application_instance_id - params[:application_instance_id] || params[:atomic_application_instance_id] + params[:application_instance_id] end def pinned_platform_guid_params diff --git a/config/routes.rb b/config/routes.rb index c292ebf..dd42120 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,30 +1,28 @@ AtomicAdmin::Engine.routes.draw do - resources :atomic_lti_platform - resources :atomic_lti_install - resources :atomic_tenant_deployment - post '/atomic_tenant_deployment/search', to: 'atomic_tenant_deployment#search' + namespace :api do + namespace :admin do + namespace :v1 do + resources :lti_platforms + resources :lti_installs + resources :tenant_deployments + resources :sites - resources :atomic_tenant_platform_guid_strategy - post '/atomic_tenant_platform_guid_strategy/search', to: 'atomic_tenant_platform_guid_strategy#search' - post '/atomic_tenant_client_id_strategy/search', to: 'atomic_tenant_client_id_strategy#search' + resources :applications do + member do + get :interactions + end - resources :atomic_tenant_client_id_strategy + resources :application_instances do + member do + get :interactions + end - resources :atomic_sites - - resources :atomic_applications do - member do - get :interactions - end - - resources :atomic_application_instances do - member do - get :interactions + resources :tenant_client_id_strategies + resources :tenant_platform_guid_strategies + resources :tenant_deployments + end + end end - - resources :atomic_client_id_strategies, controller: 'atomic_tenant_client_id_strategy' - resources :atomic_platform_guid_strategies, controller: 'atomic_tenant_platform_guid_strategy' - resources :atomic_deployments, controller: 'atomic_tenant_deployment' end end end diff --git a/lib/generators/atomic_admin/templates/controller.rb.erb b/lib/generators/atomic_admin/templates/controller.rb.erb index feccde0..20914ba 100644 --- a/lib/generators/atomic_admin/templates/controller.rb.erb +++ b/lib/generators/atomic_admin/templates/controller.rb.erb @@ -1,3 +1,3 @@ -class Api::Admin::<%= @version.classify %>::<%= @controller.classify %>Controller < AtomicAdmin::<%= @controller.classify %>Controller +class Api::Admin::<%= @version.classify %>::<%= @controller.camelize %>Controller < AtomicAdmin::<%= @controller.camelize %>Controller # Override required methods end From 855faca9599b5be1c613dde117f59723271e3c8c Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Fri, 28 Mar 2025 13:24:04 -0600 Subject: [PATCH 38/61] removed generator --- .../atomic_admin/install_generator.rb | 38 ------------------- .../atomic_admin/templates/controller.rb.erb | 3 -- 2 files changed, 41 deletions(-) delete mode 100644 lib/generators/atomic_admin/install_generator.rb delete mode 100644 lib/generators/atomic_admin/templates/controller.rb.erb diff --git a/lib/generators/atomic_admin/install_generator.rb b/lib/generators/atomic_admin/install_generator.rb deleted file mode 100644 index 5a67edf..0000000 --- a/lib/generators/atomic_admin/install_generator.rb +++ /dev/null @@ -1,38 +0,0 @@ -require "rails/generators" - -module AtomicAdmin - class InstallGenerator < Rails::Generators::Base - source_root File.expand_path('templates', __dir__) - - class_option :version, type: :string, default: 'v1', desc: 'API version (e.g. v1, v2). Defaults to v1' - - def validate_version - unless options[:version].match?(/^v\d+$/) - raise ArgumentError, 'Version must be in the format "v1", "v2", etc.' - end - end - - def create_admin_api_controllers - @version = options[:version] - - controllers = [ - 'applications', - 'application_instances', - 'lti_platforms', - 'lti_installs', - 'tenant_deployments', - 'tenant_platform_guid_strategies', - 'tenant_client_id_strategies', - 'sites' - ] - - controllers.each do |controller| - @controller = controller - template( - 'controller.rb.erb', - "app/controllers/api/admin/#{options[:version]}/#{controller}_controller.rb", - ) - end - end - end -end diff --git a/lib/generators/atomic_admin/templates/controller.rb.erb b/lib/generators/atomic_admin/templates/controller.rb.erb deleted file mode 100644 index 20914ba..0000000 --- a/lib/generators/atomic_admin/templates/controller.rb.erb +++ /dev/null @@ -1,3 +0,0 @@ -class Api::Admin::<%= @version.classify %>::<%= @controller.camelize %>Controller < AtomicAdmin::<%= @controller.camelize %>Controller - # Override required methods -end From 3e9b358c3452ad042083b3e33fc21a77a1ed1239 Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Tue, 1 Apr 2025 14:36:39 -0600 Subject: [PATCH 39/61] feat: stats endpoint --- .../atomic_admin/v1/application_instances_controller.rb | 4 ++++ config/routes.rb | 1 + 2 files changed, 5 insertions(+) diff --git a/app/controllers/atomic_admin/v1/application_instances_controller.rb b/app/controllers/atomic_admin/v1/application_instances_controller.rb index 8d7d3ed..6d9eb93 100644 --- a/app/controllers/atomic_admin/v1/application_instances_controller.rb +++ b/app/controllers/atomic_admin/v1/application_instances_controller.rb @@ -22,6 +22,10 @@ def index } end + def stats + render json: { stats: [] } + end + def show @application_instance = ApplicationInstance.find(params[:id]) render json: { application_instance: json_for(@application_instance) } diff --git a/config/routes.rb b/config/routes.rb index dd42120..dd6a78e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -15,6 +15,7 @@ resources :application_instances do member do get :interactions + get :stats end resources :tenant_client_id_strategies From df03ad742df0a0a985db95f75922163444bd8648 Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Thu, 24 Apr 2025 11:47:49 -0600 Subject: [PATCH 40/61] feat: add lti_config_xml to app instance JSON response --- .../v1/application_instances_controller.rb | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/app/controllers/atomic_admin/v1/application_instances_controller.rb b/app/controllers/atomic_admin/v1/application_instances_controller.rb index 6d9eb93..9ca451f 100644 --- a/app/controllers/atomic_admin/v1/application_instances_controller.rb +++ b/app/controllers/atomic_admin/v1/application_instances_controller.rb @@ -74,11 +74,12 @@ def interactions def json_for(instance) json = instance.as_json(include: [:application, :site]) - json["trial_start_date"] = instance.trial_start_date&.strftime("%Y-%m-%d") - json["trial_end_date"] = instance.trial_end_date&.strftime("%Y-%m-%d") - json["license_start_date"] = instance.license_start_date&.strftime("%Y-%m-%d") - json["license_end_date"] = instance.license_end_date&.strftime("%Y-%m-%d") - json["is_paid"] = instance.paid_at.present? + json["trial_start_date"] = instance.trial_start_date&.strftime("%Y-%m-%d") if instance.respond_to?(:trial_start_date) + json["trial_end_date"] = instance.trial_end_date&.strftime("%Y-%m-%d") if instance.respond_to?(:trial_end_date) + json["license_start_date"] = instance.license_start_date&.strftime("%Y-%m-%d") if instance.respond_to?(:license_start_date) + json["license_end_date"] = instance.license_end_date&.strftime("%Y-%m-%d") if instance.respond_to?(:license_end_date) + json["is_paid"] = instance.paid_at.present? if instance.respond_to?(:paid_at) + json["lti_config_xml"] = instance.lti_config_xml if instance.respond_to?(:lti_config_xml) json end From d0daa2e55f124ac7741f189f863a7ebaeb57f1f1 Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Thu, 24 Apr 2025 12:26:20 -0600 Subject: [PATCH 41/61] added some basic docs for using the gem --- docs/interactions.md | 119 +++++++++++++++++++++++++++++++++ docs/overriding-controllers.md | 20 ++++++ 2 files changed, 139 insertions(+) create mode 100644 docs/interactions.md create mode 100644 docs/overriding-controllers.md diff --git a/docs/interactions.md b/docs/interactions.md new file mode 100644 index 0000000..23072ce --- /dev/null +++ b/docs/interactions.md @@ -0,0 +1,119 @@ +# Interactions + +## `analytics` + +Display a custom analytics dashboard. + +```ruby +config.application_instance_interactions.tap do |inter| + inter.add( + :analytics, + type: :analytics, + title: "Analytics", + icon: "bar_chart", + ) +end +``` + +## `jsonform` + +Display a custom JSON form using [JSONForms](https://jsonforms.io/). + +```ruby +config.application_instance_interactions.tap do |inter| + inter.add( + :general_settings, + type: :jsonform, + title: "General Settings", + icon: "settings", + schema: AtomicAdmin::Schema::ApplicationInstanceGeneralSettingsSchema, + ) +end +``` + +### Pre-defined Schemas + +AtomicAdmin provides several pre-defined schemas that can be used with the `jsonform` interaction: + +- **ApplicationInstanceGeneralSettingsSchema**: General settings for an application instance, including nickname, primary contact, LMS URL, domain settings, and LTI configurations. + +- **ApplicationInstanceLicenseDetailsSchema**: Manage license details including paid status, license dates, number of licensed users, and license type (monthly, yearly, or FTE). + +- **ApplicationInstanceTrialDetailsSchema**: Configure trial details including start/end dates, number of trial users, and trial notes. + +- **ApplicationInstanceXmlConfigSchema**: Manage LTI 1.1 configurations, including key, secret, and XML configuration. + +- **ApplicationInstanceConfigurationSchema**: Edit custom application instance configuration and LTI configuration as JSON. + +- **AtomicApplicationUpdateSchema**: Update application settings including description, OAuth key, and OAuth secret. + +- **ApplicationInstanceCreateSchema**: Create a new application instance with basic settings like nickname, primary contact, LTI key, and site. + +### Creating Custom Schemas + +You can create your own schema by defining two methods: `schema` and `uischema`. The `schema` method defines the JSON schema, while the `uischema` method defines the UI schema. + +```ruby +class YourCustomSchema + def schema + # Define your JSON Schema here + { + type: "object", + properties: { + field_one: { + type: "string", + minLength: 1, + }, + field_two: { + type: ["string", "null"], + }, + # Add more fields as needed + }, + required: ["field_one"], + } + end + + def uischema + # Define your UI Schema here (layout) + { + type: "VerticalLayout", + elements: [ + { + type: "Control", + scope: "#/properties/field_one", + options: { + # Optional formatting options + format: "textarea", + }, + }, + { + type: "Control", + scope: "#/properties/field_two", + }, + ] + } + end +end +``` + +For more complex schemas, you can: + +- Use nested layouts with `Group`, `VerticalLayout`, and `HorizontalLayout` +- Add custom formatting with the `options` property +- Define field validation with JSON schema properties like `minLength`, `pattern`, etc. +- Use `oneOf` with mapped values for dropdowns and radio buttons + +## `lti_advantage` + +Displays a page for managing pinned Client Ids, Deployments, and Platform Instance GUIDS + +```ruby +config.application_instance_interactions.tap do |inter| + inter.add( + :lti_advantage, + type: :lti_advantage, + title: "LTI Advantage", + icon: "settings", + ) +end +``` diff --git a/docs/overriding-controllers.md b/docs/overriding-controllers.md new file mode 100644 index 0000000..852ee35 --- /dev/null +++ b/docs/overriding-controllers.md @@ -0,0 +1,20 @@ +# Overriding Controllers + +The controllers that the gem provides are designed to be overridable. + +To override the `ApplicationInstancesController` you create a new controller at the correct path, and can inherit from the gem's controller. For example: + +```ruby +# app/controllers/atomic_admin/api/admin/v1/application_instances_controller.rb + +class AtomicAdmin::Api::Admin::V1::ApplicationInstancesController < AtomicAdmin::V1::ApplicationInstancesController + # Override the index action + def index + # Custom logic here + super # Call the original method if needed + end + + # Add any other custom actions or overrides here +end +``` + From b988aff80ae618a6784855505c2accf33a8f9b12 Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Thu, 24 Apr 2025 13:17:14 -0600 Subject: [PATCH 42/61] fix: fixed LTI 1.3 controllers --- .../tenant_client_id_strategies_controller.rb | 24 ++------------ ...ant_platform_guid_strategies_controller.rb | 32 +++++-------------- app/controllers/concerns/filtering.rb | 2 +- 3 files changed, 12 insertions(+), 46 deletions(-) diff --git a/app/controllers/atomic_admin/v1/tenant_client_id_strategies_controller.rb b/app/controllers/atomic_admin/v1/tenant_client_id_strategies_controller.rb index 6c1338a..951ff56 100644 --- a/app/controllers/atomic_admin/v1/tenant_client_id_strategies_controller.rb +++ b/app/controllers/atomic_admin/v1/tenant_client_id_strategies_controller.rb @@ -1,28 +1,10 @@ -module AtomicAdmin - class AtomicTenantClientIdStrategyController < ApplicationController - - if AtomicAdmin.client_id_strategy_before_action.present? - before_action AtomicAdmin.client_id_strategy_before_action, only: [:create, :update] - end - - def pinned_client_id_params - params.permit(:iss, :client_id, :application_instance_id) - end +module AtomicAdmin::V1 + class TenantClientIdStrategiesController < AdminController + include Filtering allowed_search_columns %w[client_id] allowed_sort_columns %w[client_id] - def search - page = AtomicTenant::PinnedClientId - .where(application_instance_id: params[:application_instance_id]) - .order(:id).paginate(page: params[:page], per_page: 30) - render json: { - pinned_client_ids: page, - page: params[:page], - total_pages: page.total_pages - } - end - def index query = AtomicTenant::PinnedClientId.where(application_instance_id:) page, meta = filter(query) diff --git a/app/controllers/atomic_admin/v1/tenant_platform_guid_strategies_controller.rb b/app/controllers/atomic_admin/v1/tenant_platform_guid_strategies_controller.rb index 2f9909d..9e876de 100644 --- a/app/controllers/atomic_admin/v1/tenant_platform_guid_strategies_controller.rb +++ b/app/controllers/atomic_admin/v1/tenant_platform_guid_strategies_controller.rb @@ -1,36 +1,20 @@ -module AtomicAdmin - class AtomicTenantPlatformGuidStrategyController < ApplicationController - if AtomicAdmin.platform_guid_strategy_before_action.present? - before_action AtomicAdmin.platform_guid_strategy_before_action, only: [:create, :update] - end - - def pinned_platform_guid_params - params.permit(:iss, :platform_guid, :application_id, :application_instance_id) - end +module AtomicAdmin::V1 + class TenantPlatformGuidStrategiesController < AdminController + include Filtering allowed_search_columns %w[platform_guid] allowed_sort_columns %w[platform_guid] - def search - page = AtomicTenant::PinnedPlatformGuid - .where(application_instance_id: params[:application_instance_id]) - .order(:id).paginate(page: params[:page], per_page: 30) + def index + query = AtomicTenant::PinnedPlatformGuid.where(application_instance_id:) + page, meta = filter(query) + render json: { pinned_platform_guids: page, - page: params[:page], - total_pages: page.total_pages + meta: } end - # def index - # page = AtomicTenant::PinnedPlatformGuid.all.order(:id).paginate(page: params[:page], per_page: 30) - # render json: { - # pinned_platform_guids: page, - # page: params[:page], - # total_pages: page.total_pages - # } - # end - def create result = AtomicTenant::PinnedPlatformGuid.create!({**pinned_platform_guid_params, application_instance_id:, application_id:}) render json: { pinned_platform_guid: result } diff --git a/app/controllers/concerns/filtering.rb b/app/controllers/concerns/filtering.rb index aab3163..f396af4 100644 --- a/app/controllers/concerns/filtering.rb +++ b/app/controllers/concerns/filtering.rb @@ -26,7 +26,7 @@ def filter(relation) params = query_params allowed_search_columns = self.class.class_variable_get(:@@allowed_search_columns) - if params[:search].present? && params[:search_on].present? && allowed_search_columns + if params[:search].present? && params[:search_on].present? && allowed_search_columns.include?(params[:search_on]) relation = relation.where("lower(#{params[:search_on]}) LIKE ?", "%#{params[:search].downcase}%") end From 284287f7a1acbb6b33adcfedcc50f783c8679794 Mon Sep 17 00:00:00 2001 From: Ryker Blunck Date: Thu, 24 Apr 2025 14:46:02 -0600 Subject: [PATCH 43/61] support allowing additional app instance params --- .../atomic_admin/v1/application_instances_controller.rb | 1 + lib/atomic_admin.rb | 1 + 2 files changed, 2 insertions(+) diff --git a/app/controllers/atomic_admin/v1/application_instances_controller.rb b/app/controllers/atomic_admin/v1/application_instances_controller.rb index 9ca451f..3505922 100644 --- a/app/controllers/atomic_admin/v1/application_instances_controller.rb +++ b/app/controllers/atomic_admin/v1/application_instances_controller.rb @@ -140,6 +140,7 @@ def application_instance_params :licensed_users, :license_start_date, :license_end_date, + *AtomicAdmin.application_instance_extra_permitted_params, ) end end diff --git a/lib/atomic_admin.rb b/lib/atomic_admin.rb index e9dfd56..96bdc39 100644 --- a/lib/atomic_admin.rb +++ b/lib/atomic_admin.rb @@ -10,6 +10,7 @@ module AtomicAdmin mattr_accessor :internal_secret mattr_accessor :application_interactions, default: AtomicAdmin::Interaction::Manager.new mattr_accessor :application_instance_interactions, default: AtomicAdmin::Interaction::Manager.new + mattr_accessor :application_instance_extra_permitted_params, default: [] def self.configure yield self From addcf8cc3120ae39fe0a6b40224eca6f3d03c749 Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Tue, 6 May 2025 15:27:56 -0600 Subject: [PATCH 44/61] feat: add delete action to AppInstance controller --- .../atomic_admin/v1/application_instances_controller.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/controllers/atomic_admin/v1/application_instances_controller.rb b/app/controllers/atomic_admin/v1/application_instances_controller.rb index 3505922..c62d4f9 100644 --- a/app/controllers/atomic_admin/v1/application_instances_controller.rb +++ b/app/controllers/atomic_admin/v1/application_instances_controller.rb @@ -65,6 +65,12 @@ def update end end + def destroy + instance = ApplicationInstance.find(params[:id]) + instance.destroy + render json: { success: true } + end + def interactions instance = ApplicationInstance.find(params[:id]) interactions = AtomicAdmin.application_instance_interactions.resolve(application_instance: instance) From e9a7eb24284beac3381ffbeff12e3e92dd051a94 Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Thu, 8 May 2025 17:56:44 -0600 Subject: [PATCH 45/61] feat: generalized additional params --- .../atomic_admin/v1/application_instances_controller.rb | 1 + lib/atomic_admin.rb | 1 + 2 files changed, 2 insertions(+) diff --git a/app/controllers/atomic_admin/v1/application_instances_controller.rb b/app/controllers/atomic_admin/v1/application_instances_controller.rb index c62d4f9..35f6cfb 100644 --- a/app/controllers/atomic_admin/v1/application_instances_controller.rb +++ b/app/controllers/atomic_admin/v1/application_instances_controller.rb @@ -147,6 +147,7 @@ def application_instance_params :license_start_date, :license_end_date, *AtomicAdmin.application_instance_extra_permitted_params, + *AtomicAdmin.extra_params[:application_instances] || [], ) end end diff --git a/lib/atomic_admin.rb b/lib/atomic_admin.rb index 96bdc39..b479452 100644 --- a/lib/atomic_admin.rb +++ b/lib/atomic_admin.rb @@ -11,6 +11,7 @@ module AtomicAdmin mattr_accessor :application_interactions, default: AtomicAdmin::Interaction::Manager.new mattr_accessor :application_instance_interactions, default: AtomicAdmin::Interaction::Manager.new mattr_accessor :application_instance_extra_permitted_params, default: [] + mattr_accessor :extra_params, default: {} def self.configure yield self From 8fa981ae22fe0b20723d23bcd8036f05a841ba38 Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Thu, 8 May 2025 18:12:26 -0600 Subject: [PATCH 46/61] feat: params are fully permissive by default --- .../atomic_admin/v1/admin_controller.rb | 19 ++++--- .../v1/application_instances_controller.rb | 39 ++++---------- .../v1/applications_controller.rb | 8 +-- .../v1/lti_installs_controller.rb | 26 +++++---- .../v1/lti_platforms_controller.rb | 14 +++-- .../atomic_admin/v1/sites_controller.rb | 6 +-- .../tenant_client_id_strategies_controller.rb | 15 ++---- .../v1/tenant_deployments_controller.rb | 54 ++----------------- ...ant_platform_guid_strategies_controller.rb | 14 +++-- lib/atomic_admin.rb | 2 - 10 files changed, 70 insertions(+), 127 deletions(-) diff --git a/app/controllers/atomic_admin/v1/admin_controller.rb b/app/controllers/atomic_admin/v1/admin_controller.rb index 519dbdd..7453746 100644 --- a/app/controllers/atomic_admin/v1/admin_controller.rb +++ b/app/controllers/atomic_admin/v1/admin_controller.rb @@ -8,6 +8,17 @@ def record_not_found render_error(:not_found) end + + protected + + def json_for(resource) + resource.as_json + end + + def json_for_collection(collection) + collection.map { |resource| json_for(resource) } + end + private def render_error(type, message: nil) @@ -25,14 +36,6 @@ def render_error(type, message: nil) render json: error, status: status end - def json_for(resource) - resource.as_json - end - - def json_for_collection(collection) - collection.map { |resource| json_for(resource) } - end - def only_admins! return if is_atomic_admin? diff --git a/app/controllers/atomic_admin/v1/application_instances_controller.rb b/app/controllers/atomic_admin/v1/application_instances_controller.rb index 35f6cfb..957dbd9 100644 --- a/app/controllers/atomic_admin/v1/application_instances_controller.rb +++ b/app/controllers/atomic_admin/v1/application_instances_controller.rb @@ -33,7 +33,7 @@ def show def create application = Application.find(params[:application_id]) - instance = application.application_instances.new(application_instance_params) + instance = application.application_instances.new(create_params) if instance.save render json: { application_instance: json_for(instance) } @@ -44,7 +44,7 @@ def create def update instance = ApplicationInstance.find(params[:id]) - instance.update(application_instance_params) + instance.update(update_params) instance.update( config: params[:config], @@ -90,7 +90,7 @@ def json_for(instance) json end - private + protected def sortable_columns [ @@ -122,33 +122,12 @@ def search params[:search] end - def application_instance_params - params.permit( - :site_id, - :lti_secret, - :lti_key, - :canvas_token, - :disabled_at, - :anonymous, - :rollbar_enabled, - :paid_at, - :domain, - :use_scoped_developer_key, - :language, - :nickname, - :primary_contact, - :trial_notes, - :trial_users, - :trial_end_date, - :trial_start_date, - :license_type, - :license_notes, - :licensed_users, - :license_start_date, - :license_end_date, - *AtomicAdmin.application_instance_extra_permitted_params, - *AtomicAdmin.extra_params[:application_instances] || [], - ) + def create_params + params.permit! + end + + def update_params + params.permit! end end end diff --git a/app/controllers/atomic_admin/v1/applications_controller.rb b/app/controllers/atomic_admin/v1/applications_controller.rb index 3facb4f..9556c9f 100644 --- a/app/controllers/atomic_admin/v1/applications_controller.rb +++ b/app/controllers/atomic_admin/v1/applications_controller.rb @@ -22,7 +22,7 @@ def update @application.default_config = params[:default_config] @application.canvas_api_permissions = params[:canvas_api_permissions] - @application.update!(application_params) + @application.update!(update_params) render json: { application: json_for(@application) } end @@ -40,10 +40,10 @@ def json_for(application) json end - private + protected - def application_params - params.permit(:name, :description, :oauth_key, :oauth_secret) + def update_params + params.permit! end end end diff --git a/app/controllers/atomic_admin/v1/lti_installs_controller.rb b/app/controllers/atomic_admin/v1/lti_installs_controller.rb index bc97ef5..dcdb6ad 100644 --- a/app/controllers/atomic_admin/v1/lti_installs_controller.rb +++ b/app/controllers/atomic_admin/v1/lti_installs_controller.rb @@ -1,19 +1,11 @@ module AtomicAdmin::V1 class LtiInstallsController < AdminController - def install_params - params.permit(:iss, :client_id) - end - - def find_install - AtomicLti::Install.find_by(id: params[:id]) - end - def index render json: AtomicLti::Install.all.order(:id).paginate(page: params[:page], per_page: 30) end def create - AtomicLti::Install.create!(install_params) + AtomicLti::Install.create!(create_params) end def show @@ -23,7 +15,7 @@ def show def update install = find_install - result = install.update!(install_params) + result = install.update!(update_params) render json: result end @@ -32,5 +24,19 @@ def destroy install.destroy render json: install end + + protected + + def find_install + AtomicLti::Install.find_by(id: params[:id]) + end + + def create_params + params.permit! + end + + def update_params + params.permit! + end end end diff --git a/app/controllers/atomic_admin/v1/lti_platforms_controller.rb b/app/controllers/atomic_admin/v1/lti_platforms_controller.rb index fe6df53..6d5b57a 100644 --- a/app/controllers/atomic_admin/v1/lti_platforms_controller.rb +++ b/app/controllers/atomic_admin/v1/lti_platforms_controller.rb @@ -12,7 +12,7 @@ def index end def create - platform = AtomicLti::Platform.create!(platform_params) + platform = AtomicLti::Platform.create!(create_params) render json: { platform: platform } end @@ -23,7 +23,7 @@ def show def update platform = find_platform - platform.update!(platform_params) + platform.update!(update_params) render json: { platform: find_platform } end @@ -33,10 +33,14 @@ def destroy render json: platform end - private + protected - def platform_params - params.permit(:iss, :jwks_url, :token_url, :oidc_url) + def create_params + params.permit! + end + + def update_params + params.permit! end def find_platform diff --git a/app/controllers/atomic_admin/v1/sites_controller.rb b/app/controllers/atomic_admin/v1/sites_controller.rb index 65111f1..9ccca87 100644 --- a/app/controllers/atomic_admin/v1/sites_controller.rb +++ b/app/controllers/atomic_admin/v1/sites_controller.rb @@ -28,18 +28,18 @@ def destroy render json: { site: json_for(@site) } end - private + protected def json_for(site) site.as_json end def create_params - params.permit(:url, :oauth_key, :oauth_secret) + params.permit! end def update_params - params.permit(:url, :oauth_key, :oauth_secret) + params.permit! end end end diff --git a/app/controllers/atomic_admin/v1/tenant_client_id_strategies_controller.rb b/app/controllers/atomic_admin/v1/tenant_client_id_strategies_controller.rb index 951ff56..9308756 100644 --- a/app/controllers/atomic_admin/v1/tenant_client_id_strategies_controller.rb +++ b/app/controllers/atomic_admin/v1/tenant_client_id_strategies_controller.rb @@ -21,31 +21,24 @@ def show end def create - result = AtomicTenant::PinnedClientId.create!({**pinned_client_id_params, application_instance_id:}) + result = AtomicTenant::PinnedClientId.create!({**create_params, application_instance_id:}) render json: { pinned_client_id: result } end - # def update - # pinned_client_id = find_pinned_client_id - # pinned_client_id.update!(pinned_client_id_params) - - # render json: { pinned_client_id: find_pinned_client_id } - # end - def destroy pinned_client_id = find_pinned_client_id pinned_client_id.destroy render json: { pinned_client_id: pinned_client_id } end - private + protected def application_instance_id params[:application_instance_id] || params[:application_instance_id] end - def pinned_client_id_params - params.permit(:iss, :client_id, :application_instance_id) + def create_params + params.permit! end def find_pinned_client_id diff --git a/app/controllers/atomic_admin/v1/tenant_deployments_controller.rb b/app/controllers/atomic_admin/v1/tenant_deployments_controller.rb index d182bdf..11b4e1e 100644 --- a/app/controllers/atomic_admin/v1/tenant_deployments_controller.rb +++ b/app/controllers/atomic_admin/v1/tenant_deployments_controller.rb @@ -5,43 +5,6 @@ class TenantDeploymentsController < AdminController allowed_search_columns %w[deployment_id] allowed_sort_columns %w[deployment_id] - # NOTE: This endpoint is deprecated & only used by the legacy admin panel - def search - tenant_deployments = AtomicTenant::LtiDeployment. - where(application_instance_id: params[:application_instance_id]). - joins("LEFT OUTER JOIN public.atomic_lti_deployments"\ - " ON atomic_tenant_lti_deployments.iss = atomic_lti_deployments.iss"\ - " AND atomic_tenant_lti_deployments.deployment_id = atomic_lti_deployments.deployment_id"). - order(:id). - paginate(page: params[:page], per_page: 30) - - rows = tenant_deployments.pluck( - "atomic_tenant_lti_deployments.id", - "atomic_tenant_lti_deployments.iss", - "atomic_tenant_lti_deployments.deployment_id", - "atomic_tenant_lti_deployments.application_instance_id", - "atomic_lti_deployments.client_id", - "atomic_lti_deployments.platform_guid", - ) - - page = rows.map do |row| - { - id: row[0], - iss: row[1], - deployment_id: row[2], - application_instance_id: row[3], - client_id: row[4], - platform_guid: row[5], - } - end - - render json: { - deployments: page, - page: params[:page], - total_pages: tenant_deployments.total_pages, - } - end - def index page, meta = filter(AtomicTenant::LtiDeployment.where(application_instance_id:)) @@ -52,7 +15,7 @@ def index end def create - result = AtomicTenant::LtiDeployment.create!({**deployment_params, application_instance_id:}) + result = AtomicTenant::LtiDeployment.create!({**create_params, application_instance_id:}) render json: { deployment: result } end @@ -61,27 +24,20 @@ def show render json: { deployment: deployment } end - # def update - # deployment = find_deployment - # deployment.update!(deployment_params) - - # render json: {deployment: find_deployment} - # end - def destroy deployment = find_deployment deployment.destroy render json: { deployment: deployment } end - private + protected def application_instance_id - params[:application_instance_id] || params[:application_instance_id] + params[:application_instance_id] end - def deployment_params - params.permit(:iss, :deployment_id, :application_instance_id) + def create_params + params.permit! end def find_deployment diff --git a/app/controllers/atomic_admin/v1/tenant_platform_guid_strategies_controller.rb b/app/controllers/atomic_admin/v1/tenant_platform_guid_strategies_controller.rb index 9e876de..d7d12d0 100644 --- a/app/controllers/atomic_admin/v1/tenant_platform_guid_strategies_controller.rb +++ b/app/controllers/atomic_admin/v1/tenant_platform_guid_strategies_controller.rb @@ -16,7 +16,7 @@ def index end def create - result = AtomicTenant::PinnedPlatformGuid.create!({**pinned_platform_guid_params, application_instance_id:, application_id:}) + result = AtomicTenant::PinnedPlatformGuid.create!({**create_params, application_instance_id:, application_id:}) render json: { pinned_platform_guid: result } end @@ -27,7 +27,7 @@ def show def update pinned_platform_guid = find_pinned_platform_guid - pinned_platform_guid.update!(pinned_platform_guid_params) + pinned_platform_guid.update!(update_params) render json: {pinned_platform_guid: find_pinned_platform_guid} end @@ -38,7 +38,7 @@ def destroy render json: { pinned_platform_guid: pinned_platform_guid } end - private + protected def application_id params[:application_id] @@ -48,8 +48,12 @@ def application_instance_id params[:application_instance_id] end - def pinned_platform_guid_params - params.permit(:iss, :platform_guid, :application_id, :application_instance_id) + def create_params + params.permit! + end + + def update_params + params.permit! end def find_pinned_platform_guid diff --git a/lib/atomic_admin.rb b/lib/atomic_admin.rb index b479452..e9dfd56 100644 --- a/lib/atomic_admin.rb +++ b/lib/atomic_admin.rb @@ -10,8 +10,6 @@ module AtomicAdmin mattr_accessor :internal_secret mattr_accessor :application_interactions, default: AtomicAdmin::Interaction::Manager.new mattr_accessor :application_instance_interactions, default: AtomicAdmin::Interaction::Manager.new - mattr_accessor :application_instance_extra_permitted_params, default: [] - mattr_accessor :extra_params, default: {} def self.configure yield self From 86d69806d4994200dc30c9119e50ed0629aa5551 Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Thu, 8 May 2025 18:35:33 -0600 Subject: [PATCH 47/61] permitt all fields nested in the resource --- .../atomic_admin/v1/application_instances_controller.rb | 5 +++-- app/controllers/atomic_admin/v1/applications_controller.rb | 2 +- app/controllers/atomic_admin/v1/lti_installs_controller.rb | 4 ++-- app/controllers/atomic_admin/v1/lti_platforms_controller.rb | 4 ++-- app/controllers/atomic_admin/v1/sites_controller.rb | 4 ++-- .../v1/tenant_client_id_strategies_controller.rb | 2 +- .../atomic_admin/v1/tenant_deployments_controller.rb | 2 +- .../v1/tenant_platform_guid_strategies_controller.rb | 4 ++-- 8 files changed, 14 insertions(+), 13 deletions(-) diff --git a/app/controllers/atomic_admin/v1/application_instances_controller.rb b/app/controllers/atomic_admin/v1/application_instances_controller.rb index 957dbd9..42363a0 100644 --- a/app/controllers/atomic_admin/v1/application_instances_controller.rb +++ b/app/controllers/atomic_admin/v1/application_instances_controller.rb @@ -46,6 +46,7 @@ def update instance = ApplicationInstance.find(params[:id]) instance.update(update_params) + # Handle special params separately instance.update( config: params[:config], lti_config: params[:lti_config], @@ -123,11 +124,11 @@ def search end def create_params - params.permit! + params.require(:application_instance).permit! end def update_params - params.permit! + params.require(:application_instance).permit! end end end diff --git a/app/controllers/atomic_admin/v1/applications_controller.rb b/app/controllers/atomic_admin/v1/applications_controller.rb index 9556c9f..1dd8197 100644 --- a/app/controllers/atomic_admin/v1/applications_controller.rb +++ b/app/controllers/atomic_admin/v1/applications_controller.rb @@ -43,7 +43,7 @@ def json_for(application) protected def update_params - params.permit! + params.require(:application).permit! end end end diff --git a/app/controllers/atomic_admin/v1/lti_installs_controller.rb b/app/controllers/atomic_admin/v1/lti_installs_controller.rb index dcdb6ad..78840d9 100644 --- a/app/controllers/atomic_admin/v1/lti_installs_controller.rb +++ b/app/controllers/atomic_admin/v1/lti_installs_controller.rb @@ -32,11 +32,11 @@ def find_install end def create_params - params.permit! + params.require(:install).permit! end def update_params - params.permit! + params.require(:install).permit! end end end diff --git a/app/controllers/atomic_admin/v1/lti_platforms_controller.rb b/app/controllers/atomic_admin/v1/lti_platforms_controller.rb index 6d5b57a..8ef423d 100644 --- a/app/controllers/atomic_admin/v1/lti_platforms_controller.rb +++ b/app/controllers/atomic_admin/v1/lti_platforms_controller.rb @@ -36,11 +36,11 @@ def destroy protected def create_params - params.permit! + params.require(:platform).permit! end def update_params - params.permit! + params.require(:platform).permit! end def find_platform diff --git a/app/controllers/atomic_admin/v1/sites_controller.rb b/app/controllers/atomic_admin/v1/sites_controller.rb index 9ccca87..07f810f 100644 --- a/app/controllers/atomic_admin/v1/sites_controller.rb +++ b/app/controllers/atomic_admin/v1/sites_controller.rb @@ -35,11 +35,11 @@ def json_for(site) end def create_params - params.permit! + params.require(:site).permit! end def update_params - params.permit! + params.require(:site).permit! end end end diff --git a/app/controllers/atomic_admin/v1/tenant_client_id_strategies_controller.rb b/app/controllers/atomic_admin/v1/tenant_client_id_strategies_controller.rb index 9308756..5dfcd0f 100644 --- a/app/controllers/atomic_admin/v1/tenant_client_id_strategies_controller.rb +++ b/app/controllers/atomic_admin/v1/tenant_client_id_strategies_controller.rb @@ -38,7 +38,7 @@ def application_instance_id end def create_params - params.permit! + params.require(:pinned_client_id).permit! end def find_pinned_client_id diff --git a/app/controllers/atomic_admin/v1/tenant_deployments_controller.rb b/app/controllers/atomic_admin/v1/tenant_deployments_controller.rb index 11b4e1e..63edb6f 100644 --- a/app/controllers/atomic_admin/v1/tenant_deployments_controller.rb +++ b/app/controllers/atomic_admin/v1/tenant_deployments_controller.rb @@ -37,7 +37,7 @@ def application_instance_id end def create_params - params.permit! + params.require(:deployment).permit! end def find_deployment diff --git a/app/controllers/atomic_admin/v1/tenant_platform_guid_strategies_controller.rb b/app/controllers/atomic_admin/v1/tenant_platform_guid_strategies_controller.rb index d7d12d0..837b8cf 100644 --- a/app/controllers/atomic_admin/v1/tenant_platform_guid_strategies_controller.rb +++ b/app/controllers/atomic_admin/v1/tenant_platform_guid_strategies_controller.rb @@ -49,11 +49,11 @@ def application_instance_id end def create_params - params.permit! + params.require(:pinned_platform_guid).permit! end def update_params - params.permit! + params.require(:pinned_platform_guid).permit! end def find_pinned_platform_guid From 0772185f21f7e6066a8b0e83510aa789bf9c2314 Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Thu, 8 May 2025 18:52:17 -0600 Subject: [PATCH 48/61] feat: controllers scope their paylaods within a named top-level field --- .../v1/application_instances_controller.rb | 15 ++++----------- .../atomic_admin/v1/sites_controller.rb | 1 + 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/app/controllers/atomic_admin/v1/application_instances_controller.rb b/app/controllers/atomic_admin/v1/application_instances_controller.rb index 42363a0..33ec83c 100644 --- a/app/controllers/atomic_admin/v1/application_instances_controller.rb +++ b/app/controllers/atomic_admin/v1/application_instances_controller.rb @@ -46,19 +46,12 @@ def update instance = ApplicationInstance.find(params[:id]) instance.update(update_params) - # Handle special params separately - instance.update( - config: params[:config], - lti_config: params[:lti_config], - ) - - if params[:is_paid] && instance.paid_at.nil? + if params[:application_instance][:is_paid] && instance.paid_at.nil? instance.paid_at = DateTime.now - elsif params[:is_paid] == false && instance.paid_at.present? + elsif params[:application_instance][:is_paid] == false && instance.paid_at.present? instance.paid_at = nil end - if instance.save render json: { application_instance: json_for(instance) } else @@ -124,11 +117,11 @@ def search end def create_params - params.require(:application_instance).permit! + params.require(:application_instance).except(:is_paid, :lti_config_xml, :site, :application).permit! end def update_params - params.require(:application_instance).permit! + create_params end end end diff --git a/app/controllers/atomic_admin/v1/sites_controller.rb b/app/controllers/atomic_admin/v1/sites_controller.rb index 07f810f..8ba7b4e 100644 --- a/app/controllers/atomic_admin/v1/sites_controller.rb +++ b/app/controllers/atomic_admin/v1/sites_controller.rb @@ -39,6 +39,7 @@ def create_params end def update_params + binding.break params.require(:site).permit! end end From 9ff61012cbd1c6ada76dc19ef101064001a22350 Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Fri, 9 May 2025 13:48:34 -0600 Subject: [PATCH 49/61] chore: dependency upgrades --- Gemfile.lock | 252 ++++++++++++++++++++---------------- lib/atomic_admin/version.rb | 2 +- 2 files changed, 145 insertions(+), 109 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index c45fe6d..f858bbd 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,99 +1,115 @@ PATH remote: . specs: - atomic_admin (2.0.0) + atomic_admin (2.0.0.beta.1) rails (>= 7.0, < 9.0) GEM remote: https://rubygems.org/ specs: - actioncable (7.0.8.7) - actionpack (= 7.0.8.7) - activesupport (= 7.0.8.7) + actioncable (7.2.2.1) + actionpack (= 7.2.2.1) + activesupport (= 7.2.2.1) nio4r (~> 2.0) websocket-driver (>= 0.6.1) - actionmailbox (7.0.8.7) - actionpack (= 7.0.8.7) - activejob (= 7.0.8.7) - activerecord (= 7.0.8.7) - activestorage (= 7.0.8.7) - activesupport (= 7.0.8.7) - mail (>= 2.7.1) - net-imap - net-pop - net-smtp - actionmailer (7.0.8.7) - actionpack (= 7.0.8.7) - actionview (= 7.0.8.7) - activejob (= 7.0.8.7) - activesupport (= 7.0.8.7) - mail (~> 2.5, >= 2.5.4) - net-imap - net-pop - net-smtp - rails-dom-testing (~> 2.0) - actionpack (7.0.8.7) - actionview (= 7.0.8.7) - activesupport (= 7.0.8.7) - rack (~> 2.0, >= 2.2.4) + zeitwerk (~> 2.6) + actionmailbox (7.2.2.1) + actionpack (= 7.2.2.1) + activejob (= 7.2.2.1) + activerecord (= 7.2.2.1) + activestorage (= 7.2.2.1) + activesupport (= 7.2.2.1) + mail (>= 2.8.0) + actionmailer (7.2.2.1) + actionpack (= 7.2.2.1) + actionview (= 7.2.2.1) + activejob (= 7.2.2.1) + activesupport (= 7.2.2.1) + mail (>= 2.8.0) + rails-dom-testing (~> 2.2) + actionpack (7.2.2.1) + actionview (= 7.2.2.1) + activesupport (= 7.2.2.1) + nokogiri (>= 1.8.5) + racc + rack (>= 2.2.4, < 3.2) + rack-session (>= 1.0.1) rack-test (>= 0.6.3) - rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.0, >= 1.2.0) - actiontext (7.0.8.7) - actionpack (= 7.0.8.7) - activerecord (= 7.0.8.7) - activestorage (= 7.0.8.7) - activesupport (= 7.0.8.7) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + useragent (~> 0.16) + actiontext (7.2.2.1) + actionpack (= 7.2.2.1) + activerecord (= 7.2.2.1) + activestorage (= 7.2.2.1) + activesupport (= 7.2.2.1) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.0.8.7) - activesupport (= 7.0.8.7) + actionview (7.2.2.1) + activesupport (= 7.2.2.1) builder (~> 3.1) - erubi (~> 1.4) - rails-dom-testing (~> 2.0) - rails-html-sanitizer (~> 1.1, >= 1.2.0) - activejob (7.0.8.7) - activesupport (= 7.0.8.7) + erubi (~> 1.11) + rails-dom-testing (~> 2.2) + rails-html-sanitizer (~> 1.6) + activejob (7.2.2.1) + activesupport (= 7.2.2.1) globalid (>= 0.3.6) - activemodel (7.0.8.7) - activesupport (= 7.0.8.7) - activerecord (7.0.8.7) - activemodel (= 7.0.8.7) - activesupport (= 7.0.8.7) - activestorage (7.0.8.7) - actionpack (= 7.0.8.7) - activejob (= 7.0.8.7) - activerecord (= 7.0.8.7) - activesupport (= 7.0.8.7) + activemodel (7.2.2.1) + activesupport (= 7.2.2.1) + activerecord (7.2.2.1) + activemodel (= 7.2.2.1) + activesupport (= 7.2.2.1) + timeout (>= 0.4.0) + activestorage (7.2.2.1) + actionpack (= 7.2.2.1) + activejob (= 7.2.2.1) + activerecord (= 7.2.2.1) + activesupport (= 7.2.2.1) marcel (~> 1.0) - mini_mime (>= 1.1.0) - activesupport (7.0.8.7) - concurrent-ruby (~> 1.0, >= 1.0.2) + activesupport (7.2.2.1) + base64 + benchmark (>= 0.3) + bigdecimal + concurrent-ruby (~> 1.0, >= 1.3.1) + connection_pool (>= 2.2.5) + drb i18n (>= 1.6, < 2) + logger (>= 1.4.2) minitest (>= 5.1) - tzinfo (~> 2.0) + securerandom (>= 0.3) + tzinfo (~> 2.0, >= 2.0.5) atomic_lti (1.5.0) pg (~> 1.3) rails (~> 7.0) atomic_tenant (1.2.0) atomic_lti (~> 1.3) rails (~> 7.0) + base64 (0.2.0) + benchmark (0.4.0) bigdecimal (3.1.9) builder (3.3.0) - concurrent-ruby (1.3.4) + concurrent-ruby (1.3.5) + connection_pool (2.5.3) crass (1.0.6) csv (3.3.4) - date (3.3.3) - erubi (1.13.0) - globalid (1.1.0) - activesupport (>= 5.0) + date (3.4.1) + drb (2.2.1) + erubi (1.13.1) + globalid (1.2.1) + activesupport (>= 6.1) httparty (0.23.1) csv mini_mime (>= 1.0.0) multi_xml (>= 0.5.2) - i18n (1.14.6) + i18n (1.14.7) concurrent-ruby (~> 1.0) - loofah (2.23.1) + io-console (0.8.0) + irb (1.15.2) + pp (>= 0.6.0) + rdoc (>= 4.0.0) + reline (>= 0.4.2) + logger (1.7.0) + loofah (2.24.0) crass (~> 1.0.2) nokogiri (>= 1.12.0) mail (2.8.1) @@ -101,80 +117,100 @@ GEM net-imap net-pop net-smtp - marcel (1.0.2) - method_source (1.0.0) + marcel (1.0.4) mini_mime (1.1.5) - minitest (5.25.4) + minitest (5.25.5) multi_xml (0.7.2) bigdecimal (~> 3.1) - net-imap (0.3.7) + net-imap (0.5.8) date net-protocol net-pop (0.1.2) net-protocol - net-protocol (0.2.1) + net-protocol (0.2.2) timeout - net-smtp (0.3.3) + net-smtp (0.5.1) net-protocol - nio4r (2.5.9) - nokogiri (1.17.1-arm64-darwin) + nio4r (2.7.4) + nokogiri (1.18.8-arm64-darwin) racc (~> 1.4) - nokogiri (1.17.1-x86_64-darwin) + nokogiri (1.18.8-x86_64-darwin) racc (~> 1.4) - nokogiri (1.17.1-x86_64-linux) + nokogiri (1.18.8-x86_64-linux-gnu) racc (~> 1.4) - pg (1.5.3) + pg (1.5.9) + pp (0.6.2) + prettyprint + prettyprint (0.2.0) + psych (5.2.4) + date + stringio racc (1.8.1) - rack (2.2.10) - rack-test (2.1.0) + rack (3.1.14) + rack-session (2.1.1) + base64 (>= 0.1.0) + rack (>= 3.0.0) + rack-test (2.2.0) rack (>= 1.3) - rails (7.0.8.7) - actioncable (= 7.0.8.7) - actionmailbox (= 7.0.8.7) - actionmailer (= 7.0.8.7) - actionpack (= 7.0.8.7) - actiontext (= 7.0.8.7) - actionview (= 7.0.8.7) - activejob (= 7.0.8.7) - activemodel (= 7.0.8.7) - activerecord (= 7.0.8.7) - activestorage (= 7.0.8.7) - activesupport (= 7.0.8.7) + rackup (2.2.1) + rack (>= 3) + rails (7.2.2.1) + actioncable (= 7.2.2.1) + actionmailbox (= 7.2.2.1) + actionmailer (= 7.2.2.1) + actionpack (= 7.2.2.1) + actiontext (= 7.2.2.1) + actionview (= 7.2.2.1) + activejob (= 7.2.2.1) + activemodel (= 7.2.2.1) + activerecord (= 7.2.2.1) + activestorage (= 7.2.2.1) + activesupport (= 7.2.2.1) bundler (>= 1.15.0) - railties (= 7.0.8.7) + railties (= 7.2.2.1) rails-dom-testing (2.2.0) activesupport (>= 5.0.0) minitest nokogiri (>= 1.6) - rails-html-sanitizer (1.6.1) + rails-html-sanitizer (1.6.2) loofah (~> 2.21) nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) - railties (7.0.8.7) - actionpack (= 7.0.8.7) - activesupport (= 7.0.8.7) - method_source + railties (7.2.2.1) + actionpack (= 7.2.2.1) + activesupport (= 7.2.2.1) + irb (~> 1.13) + rackup (>= 1.0.0) rake (>= 12.2) - thor (~> 1.0) - zeitwerk (~> 2.5) - rake (13.0.6) - sprockets (4.2.0) + thor (~> 1.0, >= 1.2.2) + zeitwerk (~> 2.6) + rake (13.2.1) + rdoc (6.13.1) + psych (>= 4.0.0) + reline (0.6.1) + io-console (~> 0.5) + securerandom (0.4.1) + sprockets (4.2.2) concurrent-ruby (~> 1.0) + logger rack (>= 2.2.4, < 4) - sprockets-rails (3.4.2) - actionpack (>= 5.2) - activesupport (>= 5.2) + sprockets-rails (3.5.2) + actionpack (>= 6.1) + activesupport (>= 6.1) sprockets (>= 3.0.0) - sqlite3 (1.6.3-arm64-darwin) - sqlite3 (1.6.3-x86_64-darwin) - sqlite3 (1.6.3-x86_64-linux) - thor (1.2.2) - timeout (0.4.0) + sqlite3 (2.6.0-arm64-darwin) + sqlite3 (2.6.0-x86_64-darwin) + sqlite3 (2.6.0-x86_64-linux-gnu) + stringio (3.1.7) + thor (1.3.2) + timeout (0.4.3) tzinfo (2.0.6) concurrent-ruby (~> 1.0) - websocket-driver (0.7.6) + useragent (0.16.11) + websocket-driver (0.7.7) + base64 websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) - zeitwerk (2.6.11) + zeitwerk (2.7.2) PLATFORMS arm64-darwin-23 diff --git a/lib/atomic_admin/version.rb b/lib/atomic_admin/version.rb index 2dd3c1f..376009c 100644 --- a/lib/atomic_admin/version.rb +++ b/lib/atomic_admin/version.rb @@ -1,3 +1,3 @@ module AtomicAdmin - VERSION = "2.0.0" + VERSION = "2.0.0.beta.1".freeze end From 7126ed77c41f2ff4a180b0bcc40786d9a28a0991 Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Mon, 12 May 2025 09:32:58 -0600 Subject: [PATCH 50/61] feat: support searching & sorting by issuer in LTI Advantage resources --- .../atomic_admin/v1/tenant_client_id_strategies_controller.rb | 4 ++-- .../atomic_admin/v1/tenant_deployments_controller.rb | 4 ++-- .../v1/tenant_platform_guid_strategies_controller.rb | 4 ++-- lib/atomic_admin/version.rb | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/controllers/atomic_admin/v1/tenant_client_id_strategies_controller.rb b/app/controllers/atomic_admin/v1/tenant_client_id_strategies_controller.rb index 5dfcd0f..77d905e 100644 --- a/app/controllers/atomic_admin/v1/tenant_client_id_strategies_controller.rb +++ b/app/controllers/atomic_admin/v1/tenant_client_id_strategies_controller.rb @@ -2,8 +2,8 @@ module AtomicAdmin::V1 class TenantClientIdStrategiesController < AdminController include Filtering - allowed_search_columns %w[client_id] - allowed_sort_columns %w[client_id] + allowed_search_columns %w[client_id, iss] + allowed_sort_columns %w[client_id, iss] def index query = AtomicTenant::PinnedClientId.where(application_instance_id:) diff --git a/app/controllers/atomic_admin/v1/tenant_deployments_controller.rb b/app/controllers/atomic_admin/v1/tenant_deployments_controller.rb index 63edb6f..22f17f5 100644 --- a/app/controllers/atomic_admin/v1/tenant_deployments_controller.rb +++ b/app/controllers/atomic_admin/v1/tenant_deployments_controller.rb @@ -2,8 +2,8 @@ module AtomicAdmin::V1 class TenantDeploymentsController < AdminController include Filtering - allowed_search_columns %w[deployment_id] - allowed_sort_columns %w[deployment_id] + allowed_search_columns %w[deployment_id, iss] + allowed_sort_columns %w[deployment_id, iss] def index page, meta = filter(AtomicTenant::LtiDeployment.where(application_instance_id:)) diff --git a/app/controllers/atomic_admin/v1/tenant_platform_guid_strategies_controller.rb b/app/controllers/atomic_admin/v1/tenant_platform_guid_strategies_controller.rb index 837b8cf..cbe0b97 100644 --- a/app/controllers/atomic_admin/v1/tenant_platform_guid_strategies_controller.rb +++ b/app/controllers/atomic_admin/v1/tenant_platform_guid_strategies_controller.rb @@ -2,8 +2,8 @@ module AtomicAdmin::V1 class TenantPlatformGuidStrategiesController < AdminController include Filtering - allowed_search_columns %w[platform_guid] - allowed_sort_columns %w[platform_guid] + allowed_search_columns %w[platform_guid, iss] + allowed_sort_columns %w[platform_guid, iss] def index query = AtomicTenant::PinnedPlatformGuid.where(application_instance_id:) diff --git a/lib/atomic_admin/version.rb b/lib/atomic_admin/version.rb index 376009c..1e59d11 100644 --- a/lib/atomic_admin/version.rb +++ b/lib/atomic_admin/version.rb @@ -1,3 +1,3 @@ module AtomicAdmin - VERSION = "2.0.0.beta.1".freeze + VERSION = "2.0.0.beta.2".freeze end From 6645db6138bb99fa690e154728a4ae78e7317295 Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Mon, 12 May 2025 11:07:44 -0600 Subject: [PATCH 51/61] pulling in old routes --- Gemfile.lock | 2 +- .../api/admin/v0/admin_controller.rb | 10 +++ .../admin/v0/atomic_lti_install_controller.rb | 36 +++++++++ .../v0/atomic_lti_platform_controller.rb | 43 ++++++++++ ...ic_tenant_client_id_strategy_controller.rb | 59 ++++++++++++++ .../v0/atomic_tenant_deployment_controller.rb | 79 +++++++++++++++++++ ...enant_platform_guid_strategy_controller.rb | 58 ++++++++++++++ .../authenticating_application_controller.rb | 2 +- lib/atomic_admin.rb | 1 + 9 files changed, 288 insertions(+), 2 deletions(-) create mode 100644 app/controllers/atomic_admin/api/admin/v0/admin_controller.rb create mode 100644 app/controllers/atomic_admin/api/admin/v0/atomic_lti_install_controller.rb create mode 100644 app/controllers/atomic_admin/api/admin/v0/atomic_lti_platform_controller.rb create mode 100644 app/controllers/atomic_admin/api/admin/v0/atomic_tenant_client_id_strategy_controller.rb create mode 100644 app/controllers/atomic_admin/api/admin/v0/atomic_tenant_deployment_controller.rb create mode 100644 app/controllers/atomic_admin/api/admin/v0/atomic_tenant_platform_guid_strategy_controller.rb rename app/controllers/atomic_admin/{ => api/admin/v0}/authenticating_application_controller.rb (93%) diff --git a/Gemfile.lock b/Gemfile.lock index f858bbd..051b3fa 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - atomic_admin (2.0.0.beta.1) + atomic_admin (2.0.0.beta.2) rails (>= 7.0, < 9.0) GEM diff --git a/app/controllers/atomic_admin/api/admin/v0/admin_controller.rb b/app/controllers/atomic_admin/api/admin/v0/admin_controller.rb new file mode 100644 index 0000000..3a9599a --- /dev/null +++ b/app/controllers/atomic_admin/api/admin/v0/admin_controller.rb @@ -0,0 +1,10 @@ +module AtomicAdmin::Api::Admin::V0 + BASE_CONTROLLER = if AtomicAdmin.authenticating_base_controller_class + AtomicAdmin.authenticating_base_controller_class.constantize + else + AtomicAdmin::Api::Admin::V0::AuthenticatingApplicationController + end + + class AdminController < BASE_CONTROLLER + end +end diff --git a/app/controllers/atomic_admin/api/admin/v0/atomic_lti_install_controller.rb b/app/controllers/atomic_admin/api/admin/v0/atomic_lti_install_controller.rb new file mode 100644 index 0000000..d76e12e --- /dev/null +++ b/app/controllers/atomic_admin/api/admin/v0/atomic_lti_install_controller.rb @@ -0,0 +1,36 @@ +module AtomicAdmin::Api::Admin::V0 + class AtomicLtiInstallController < AdminController + def install_params + params.permit(:iss, :client_id) + end + + def find_install + AtomicLti::Install.find_by(id: params[:id]) + end + + def index + render json: AtomicLti::Install.all.order(:id).paginate(page: params[:page], per_page: 30) + end + + def create + AtomicLti::Install.create!(install_params) + end + + def show + install = find_install + render json: install + end + + def update + install = find_install + result = install.update!(install_params) + render json: result + end + + def destroy + install = find_install + install.destroy + render json: install + end + end +end diff --git a/app/controllers/atomic_admin/api/admin/v0/atomic_lti_platform_controller.rb b/app/controllers/atomic_admin/api/admin/v0/atomic_lti_platform_controller.rb new file mode 100644 index 0000000..4b1ead5 --- /dev/null +++ b/app/controllers/atomic_admin/api/admin/v0/atomic_lti_platform_controller.rb @@ -0,0 +1,43 @@ +module AtomicAdmin::Api::Admin::V0 + class AtomicLtiPlatformController < AdminController + def platform_params + params.permit(:iss, :jwks_url, :token_url, :oidc_url) + end + + def find_platform + AtomicLti::Platform.find_by(id: params[:id]) + end + + def index + page = AtomicLti::Platform.all.order(:id).paginate(page: params[:page], per_page: 30) + + render json: { + platforms: page, + page: params[:page], + total_pages: page.total_pages + } + end + + def create + platform = AtomicLti::Platform.create!(platform_params) + render json: { platform: platform } + end + + def show + platform = find_platform + render json: platform + end + + def update + platform = find_platform + platform.update!(platform_params) + render json: { platform: find_platform } + end + + def destroy + platform = find_platform + platform.destroy + render json: platform + end + end +end diff --git a/app/controllers/atomic_admin/api/admin/v0/atomic_tenant_client_id_strategy_controller.rb b/app/controllers/atomic_admin/api/admin/v0/atomic_tenant_client_id_strategy_controller.rb new file mode 100644 index 0000000..83f8c69 --- /dev/null +++ b/app/controllers/atomic_admin/api/admin/v0/atomic_tenant_client_id_strategy_controller.rb @@ -0,0 +1,59 @@ +module AtomicAdmin::Api::Admin::V0 + class AtomicTenantClientIdStrategyController < AdminController + + if AtomicAdmin.client_id_strategy_before_action.present? + before_action AtomicAdmin.client_id_strategy_before_action, only: [:create, :update] + end + + def pinned_client_id_params + params.permit(:iss, :client_id, :application_instance_id) + end + + def find_pinned_client_id + AtomicTenant::PinnedClientId.find_by(id: params[:id]) + end + + def search + page = AtomicTenant::PinnedClientId + .where(application_instance_id: params[:application_instance_id]) + .order(:id).paginate(page: params[:page], per_page: 30) + render json: { + pinned_client_ids: page, + page: params[:page], + total_pages: page.total_pages + } + end + + # def index + # page = AtomicTenant::PinnedClientId.all.order(:id).paginate(page: params[:page], per_page: 30) + # render json: { + # pinned_client_ids: page, + # page: params[:page], + # total_pages: page.total_pages + # } + # end + + def create + result = AtomicTenant::PinnedClientId.create!(pinned_client_id_params) + render json: { pinned_client_id: result } + end + + def show + pinned_client_id = find_pinned_client_id + render json: {pinned_client_id: pinned_client_id} + end + + # def update + # pinned_client_id = find_pinned_client_id + # pinned_client_id.update!(pinned_client_id_params) + + # render json: {pinned_client_id: find_pinned_client_id} + # end + + def destroy + pinned_client_id = find_pinned_client_id + pinned_client_id.destroy + render json: { pinned_client_id: pinned_client_id } + end + end +end diff --git a/app/controllers/atomic_admin/api/admin/v0/atomic_tenant_deployment_controller.rb b/app/controllers/atomic_admin/api/admin/v0/atomic_tenant_deployment_controller.rb new file mode 100644 index 0000000..c81e509 --- /dev/null +++ b/app/controllers/atomic_admin/api/admin/v0/atomic_tenant_deployment_controller.rb @@ -0,0 +1,79 @@ +module AtomicAdmin::Api::Admin::V0 + class AtomicTenantDeploymentController < AdminController + def deployment_params + params.permit(:iss, :deployment_id, :application_instance_id) + end + + def find_deployment + AtomicTenant::LtiDeployment.find_by(id: params[:id]) + end + + def search + tenant_deployments = AtomicTenant::LtiDeployment. + where(application_instance_id: params[:application_instance_id]). + joins("LEFT OUTER JOIN public.atomic_lti_deployments"\ + " ON atomic_tenant_lti_deployments.iss = atomic_lti_deployments.iss"\ + " AND atomic_tenant_lti_deployments.deployment_id = atomic_lti_deployments.deployment_id"). + order(:id). + paginate(page: params[:page], per_page: 30) + + rows = tenant_deployments.pluck( + "atomic_tenant_lti_deployments.id", + "atomic_tenant_lti_deployments.iss", + "atomic_tenant_lti_deployments.deployment_id", + "atomic_tenant_lti_deployments.application_instance_id", + "atomic_lti_deployments.client_id", + "atomic_lti_deployments.platform_guid", + ) + + page = rows.map do |row| + { + id: row[0], + iss: row[1], + deployment_id: row[2], + application_instance_id: row[3], + client_id: row[4], + platform_guid: row[5], + } + end + + render json: { + deployments: page, + page: params[:page], + total_pages: tenant_deployments.total_pages, + } + end + + # def index + # page = AtomicTenant::LtiDeployment.all.order(:id).paginate(page: params[:page], per_page: 30) + # render json: { + # deployments: page, + # page: params[:page], + # total_pages: page.total_pages + # } + # end + + def create + result = AtomicTenant::LtiDeployment.create!(deployment_params) + render json: { deployment: result } + end + + def show + deployment = find_deployment + render json: { deployment: deployment } + end + + # def update + # deployment = find_deployment + # deployment.update!(deployment_params) + + # render json: {deployment: find_deployment} + # end + + def destroy + deployment = find_deployment + deployment.destroy + render json: { deployment: deployment } + end + end +end diff --git a/app/controllers/atomic_admin/api/admin/v0/atomic_tenant_platform_guid_strategy_controller.rb b/app/controllers/atomic_admin/api/admin/v0/atomic_tenant_platform_guid_strategy_controller.rb new file mode 100644 index 0000000..19b49e7 --- /dev/null +++ b/app/controllers/atomic_admin/api/admin/v0/atomic_tenant_platform_guid_strategy_controller.rb @@ -0,0 +1,58 @@ +module AtomicAdmin::Api::Admin::V0 + class AtomicTenantPlatformGuidStrategyController < AdminController + if AtomicAdmin.platform_guid_strategy_before_action.present? + before_action AtomicAdmin.platform_guid_strategy_before_action, only: [:create, :update] + end + + def pinned_platform_guid_params + params.permit(:iss, :platform_guid, :application_id, :application_instance_id) + end + + def find_pinned_platform_guid + AtomicTenant::PinnedPlatformGuid.find(params[:id]) + end + + def search + page = AtomicTenant::PinnedPlatformGuid + .where(application_instance_id: params[:application_instance_id]) + .order(:id).paginate(page: params[:page], per_page: 30) + render json: { + pinned_platform_guids: page, + page: params[:page], + total_pages: page.total_pages + } + end + + # def index + # page = AtomicTenant::PinnedPlatformGuid.all.order(:id).paginate(page: params[:page], per_page: 30) + # render json: { + # pinned_platform_guids: page, + # page: params[:page], + # total_pages: page.total_pages + # } + # end + + def create + result = AtomicTenant::PinnedPlatformGuid.create!(pinned_platform_guid_params) + render json: { pinned_platform_guid: result } + end + + def show + pinned_platform_guid = find_pinned_platform_guid + render json: {pinned_platform_guid: pinned_platform_guid} + end + + def update + pinned_platform_guid = find_pinned_platform_guid + pinned_platform_guid.update!(pinned_platform_guid_params) + + render json: {pinned_platform_guid: find_pinned_platform_guid} + end + + def destroy + pinned_platform_guid = find_pinned_platform_guid + pinned_platform_guid.destroy + render json: { pinned_platform_guid: pinned_platform_guid } + end + end +end diff --git a/app/controllers/atomic_admin/authenticating_application_controller.rb b/app/controllers/atomic_admin/api/admin/v0/authenticating_application_controller.rb similarity index 93% rename from app/controllers/atomic_admin/authenticating_application_controller.rb rename to app/controllers/atomic_admin/api/admin/v0/authenticating_application_controller.rb index e2a9f38..c466986 100644 --- a/app/controllers/atomic_admin/authenticating_application_controller.rb +++ b/app/controllers/atomic_admin/api/admin/v0/authenticating_application_controller.rb @@ -1,4 +1,4 @@ -module AtomicAdmin +module AtomicAdmin::Api::Admin::V0 class AuthenticatingApplicationController < ActionController::API include AtomicAdmin::JwtToken # before_action :authenticate_user! # Use validate_token instead for now diff --git a/lib/atomic_admin.rb b/lib/atomic_admin.rb index e9dfd56..55806e9 100644 --- a/lib/atomic_admin.rb +++ b/lib/atomic_admin.rb @@ -10,6 +10,7 @@ module AtomicAdmin mattr_accessor :internal_secret mattr_accessor :application_interactions, default: AtomicAdmin::Interaction::Manager.new mattr_accessor :application_instance_interactions, default: AtomicAdmin::Interaction::Manager.new + mattr_accessor :authenticating_base_controller_class, default: nil def self.configure yield self From cdcceb15357d19b4fe02a56037c50a49d8b74327 Mon Sep 17 00:00:00 2001 From: David Spencer Date: Mon, 12 May 2025 14:57:06 -0400 Subject: [PATCH 52/61] Add supporting a custom analytics controller --- config/routes.rb | 1 + lib/atomic_admin/interaction.rb | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/config/routes.rb b/config/routes.rb index dd6a78e..d967c2e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -21,6 +21,7 @@ resources :tenant_client_id_strategies resources :tenant_platform_guid_strategies resources :tenant_deployments + resources :analytics end end end diff --git a/lib/atomic_admin/interaction.rb b/lib/atomic_admin/interaction.rb index 01edd96..93c7362 100644 --- a/lib/atomic_admin/interaction.rb +++ b/lib/atomic_admin/interaction.rb @@ -10,6 +10,13 @@ def add(key, **kwargs) **kwargs, order: @curr_index, } + + if @interactions[key][:type] == :analytics && @interactions[key][:controller].present? + controller_class = @interactions[key][:controller] + Rails.application.config.to_prepare do + AtomicAdmin::Api::Admin::V1.const_set(:AnalyticsController, controller_class.constantize) + end + end @curr_index += 1 end From 5f68f04ea31cee854e8940ec6e3963b9e8c9c1e5 Mon Sep 17 00:00:00 2001 From: David Spencer Date: Mon, 12 May 2025 15:23:22 -0400 Subject: [PATCH 53/61] Remove unused stats placeholder --- .../atomic_admin/v1/application_instances_controller.rb | 4 ---- config/routes.rb | 1 - 2 files changed, 5 deletions(-) diff --git a/app/controllers/atomic_admin/v1/application_instances_controller.rb b/app/controllers/atomic_admin/v1/application_instances_controller.rb index 33ec83c..5bd1958 100644 --- a/app/controllers/atomic_admin/v1/application_instances_controller.rb +++ b/app/controllers/atomic_admin/v1/application_instances_controller.rb @@ -22,10 +22,6 @@ def index } end - def stats - render json: { stats: [] } - end - def show @application_instance = ApplicationInstance.find(params[:id]) render json: { application_instance: json_for(@application_instance) } diff --git a/config/routes.rb b/config/routes.rb index d967c2e..af60f8a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -15,7 +15,6 @@ resources :application_instances do member do get :interactions - get :stats end resources :tenant_client_id_strategies From db3702e4be1fc42bde0a275f4c64eab57a4ed529 Mon Sep 17 00:00:00 2001 From: David Spencer Date: Mon, 12 May 2025 15:23:30 -0400 Subject: [PATCH 54/61] Rename analytics to stats --- config/routes.rb | 2 +- lib/atomic_admin/interaction.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/config/routes.rb b/config/routes.rb index af60f8a..f8742bd 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -20,7 +20,7 @@ resources :tenant_client_id_strategies resources :tenant_platform_guid_strategies resources :tenant_deployments - resources :analytics + resources :stats end end end diff --git a/lib/atomic_admin/interaction.rb b/lib/atomic_admin/interaction.rb index 93c7362..34fa975 100644 --- a/lib/atomic_admin/interaction.rb +++ b/lib/atomic_admin/interaction.rb @@ -14,7 +14,7 @@ def add(key, **kwargs) if @interactions[key][:type] == :analytics && @interactions[key][:controller].present? controller_class = @interactions[key][:controller] Rails.application.config.to_prepare do - AtomicAdmin::Api::Admin::V1.const_set(:AnalyticsController, controller_class.constantize) + AtomicAdmin::Api::Admin::V1.const_set(:StatsController, controller_class.constantize) end end @curr_index += 1 From 3d62a003f4e15650955a0030f621ab55d6396624 Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Mon, 12 May 2025 16:51:56 -0600 Subject: [PATCH 55/61] refactor: replace admin token validation with internal token validation in AuthenticatingApplicationController --- .../authenticating_application_controller.rb | 16 ++-------- .../atomic_admin/v1/admin_controller.rb | 9 +----- app/controllers/concerns/require_jwt_token.rb | 30 ++++--------------- 3 files changed, 8 insertions(+), 47 deletions(-) diff --git a/app/controllers/atomic_admin/api/admin/v0/authenticating_application_controller.rb b/app/controllers/atomic_admin/api/admin/v0/authenticating_application_controller.rb index c466986..5baf99e 100644 --- a/app/controllers/atomic_admin/api/admin/v0/authenticating_application_controller.rb +++ b/app/controllers/atomic_admin/api/admin/v0/authenticating_application_controller.rb @@ -1,18 +1,6 @@ module AtomicAdmin::Api::Admin::V0 class AuthenticatingApplicationController < ActionController::API - include AtomicAdmin::JwtToken - # before_action :authenticate_user! # Use validate_token instead for now - before_action :validate_token - before_action :only_admins! - - private - - def only_admins! - user_not_authorized unless current_user.admin? - end - - def user_not_authorized(message = "Not Authorized") - render json: { message: message, }, status: 401 - end + include RequireJwtToken + before_action :validate_internal_token end end diff --git a/app/controllers/atomic_admin/v1/admin_controller.rb b/app/controllers/atomic_admin/v1/admin_controller.rb index 7453746..1a77f6a 100644 --- a/app/controllers/atomic_admin/v1/admin_controller.rb +++ b/app/controllers/atomic_admin/v1/admin_controller.rb @@ -1,14 +1,13 @@ module AtomicAdmin::V1 class AdminController < ActionController::API include RequireJwtToken - before_action :only_admins! + before_action :validate_admin_token rescue_from ActiveRecord::RecordNotFound, with: :record_not_found def record_not_found render_error(:not_found) end - protected def json_for(resource) @@ -36,12 +35,6 @@ def render_error(type, message: nil) render json: error, status: status end - def only_admins! - return if is_atomic_admin? - - user_not_authorized if current_user.blank? && !current_user.admin? - end - def user_not_authorized(message = "Not Authorized") render json: { message: message, }, status: 401 end diff --git a/app/controllers/concerns/require_jwt_token.rb b/app/controllers/concerns/require_jwt_token.rb index 0df91f9..00c80b0 100644 --- a/app/controllers/concerns/require_jwt_token.rb +++ b/app/controllers/concerns/require_jwt_token.rb @@ -1,20 +1,5 @@ module RequireJwtToken - extend ActiveSupport::Concern - - included do - attr_accessor :auth_source - - before_action :validate_admin_token - before_action :validate_internal_token - end - - def is_atomic_admin? - self.auth_source == :atomic_admin - end - - def is_internal? - self.auth_source == :internal - end + extend ActiveSupport::Concern protected @@ -23,18 +8,14 @@ def validate_admin_token decoder = AtomicAdmin::JwtToken::JwksDecoder.new(AtomicAdmin.admin_jwks_url) token = decoder.decode(encoded_token)&.first validate_claims!(token) - self.auth_source = :atomic_admin - token - rescue Exception => e - # Capture all exceptions to let the internal token validation handle it - Rails.logger.error "Admin JWT Error occured #{e.inspect}" - nil + + rescue JWT::DecodeError, AtomicAdmin::JwtToken::InvalidTokenError => e + Rails.logger.error "JWT Error occured #{e.inspect}" + render json: { error: "Unauthorized: Invalid token." }, status: :unauthorized end def validate_internal_token - return if is_atomic_admin? - encoded_token = get_encoded_token(request) decoder = AtomicAdmin::JwtToken::SecretDecoder.new(AtomicAdmin.internal_secret) token = decoder.decode!(encoded_token) @@ -49,7 +30,6 @@ def validate_internal_token @user = User.find(token["user_id"]) sign_in(@user, event: :authentication, store: false) - self.auth_source = :internal rescue JWT::DecodeError, AtomicAdmin::JwtToken::InvalidTokenError => e Rails.logger.error "Internal JWT Error occured #{e.inspect}" render json: { error: "Unauthorized: Invalid token." }, status: :unauthorized From 71c9ea9444140ab86df87fa6e62d96d65a8b5857 Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Mon, 12 May 2025 17:05:51 -0600 Subject: [PATCH 56/61] feat: add old routes back to routes config --- config/routes.rb | 15 +++++++++++++++ lib/atomic_admin.rb | 2 ++ 2 files changed, 17 insertions(+) diff --git a/config/routes.rb b/config/routes.rb index dd6a78e..9cdc595 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,6 +1,21 @@ AtomicAdmin::Engine.routes.draw do namespace :api do namespace :admin do + # NOTE: these are the "legacy" routes that the old admin app relies on. They don't follow the same conventions as the new API. + # They are also not namespaces under /api/admin/v0 but rather /api/admin/* + scope module: "v0" do + resources :atomic_lti_platform + resources :atomic_lti_install + resources :atomic_tenant_deployment + post '/atomic_tenant_deployment/search', to: 'atomic_tenant_deployment#search' + + resources :atomic_tenant_platform_guid_strategy + post '/atomic_tenant_platform_guid_strategy/search', to: 'atomic_tenant_platform_guid_strategy#search' + + resources :atomic_tenant_client_id_strategy + post '/atomic_tenant_client_id_strategy/search', to: 'atomic_tenant_client_id_strategy#search' + end + namespace :v1 do resources :lti_platforms resources :lti_installs diff --git a/lib/atomic_admin.rb b/lib/atomic_admin.rb index 55806e9..b72e4c6 100644 --- a/lib/atomic_admin.rb +++ b/lib/atomic_admin.rb @@ -11,6 +11,8 @@ module AtomicAdmin mattr_accessor :application_interactions, default: AtomicAdmin::Interaction::Manager.new mattr_accessor :application_instance_interactions, default: AtomicAdmin::Interaction::Manager.new mattr_accessor :authenticating_base_controller_class, default: nil + mattr_accessor :client_id_strategy_before_action, default: nil + mattr_accessor :platform_guid_strategy_before_action, default: nil def self.configure yield self From 38da8ac833eb2b09a9be236928c3de6d0332b9ad Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Mon, 12 May 2025 17:08:11 -0600 Subject: [PATCH 57/61] chore: version bump --- Gemfile.lock | 2 +- lib/atomic_admin/version.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 051b3fa..e6ca9c4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - atomic_admin (2.0.0.beta.2) + atomic_admin (2.0.0.beta.3) rails (>= 7.0, < 9.0) GEM diff --git a/lib/atomic_admin/version.rb b/lib/atomic_admin/version.rb index 1e59d11..dfc0115 100644 --- a/lib/atomic_admin/version.rb +++ b/lib/atomic_admin/version.rb @@ -1,3 +1,3 @@ module AtomicAdmin - VERSION = "2.0.0.beta.2".freeze + VERSION = "2.0.0.beta.3".freeze end From 47356c135c503d91818c68360f08dd9ae8c59a5d Mon Sep 17 00:00:00 2001 From: Sean Collings Date: Tue, 1 Jul 2025 12:32:34 -0600 Subject: [PATCH 58/61] fix: removed debug call --- app/controllers/atomic_admin/v1/sites_controller.rb | 1 - lib/atomic_admin/version.rb | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/controllers/atomic_admin/v1/sites_controller.rb b/app/controllers/atomic_admin/v1/sites_controller.rb index 8ba7b4e..07f810f 100644 --- a/app/controllers/atomic_admin/v1/sites_controller.rb +++ b/app/controllers/atomic_admin/v1/sites_controller.rb @@ -39,7 +39,6 @@ def create_params end def update_params - binding.break params.require(:site).permit! end end diff --git a/lib/atomic_admin/version.rb b/lib/atomic_admin/version.rb index dfc0115..bf3cbc6 100644 --- a/lib/atomic_admin/version.rb +++ b/lib/atomic_admin/version.rb @@ -1,3 +1,3 @@ module AtomicAdmin - VERSION = "2.0.0.beta.3".freeze + VERSION = "2.0.0.beta.4".freeze end From 0ff0ea06109669c94d1ae211607d1a7583874f28 Mon Sep 17 00:00:00 2001 From: Ryker Blunck Date: Thu, 23 Oct 2025 17:52:03 -0600 Subject: [PATCH 59/61] WIP support integration between new admin panel and catalyst --- Gemfile.lock | 11 ++++++----- lib/atomic_admin/interaction.rb | 3 +++ 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index e6ca9c4..47fbe49 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - atomic_admin (2.0.0.beta.3) + atomic_admin (2.0.0.beta.4) rails (>= 7.0, < 9.0) GEM @@ -120,7 +120,7 @@ GEM marcel (1.0.4) mini_mime (1.1.5) minitest (5.25.5) - multi_xml (0.7.2) + multi_xml (0.7.1) bigdecimal (~> 3.1) net-imap (0.5.8) date @@ -136,7 +136,7 @@ GEM racc (~> 1.4) nokogiri (1.18.8-x86_64-darwin) racc (~> 1.4) - nokogiri (1.18.8-x86_64-linux-gnu) + nokogiri (1.18.8-x86_64-linux) racc (~> 1.4) pg (1.5.9) pp (0.6.2) @@ -199,7 +199,7 @@ GEM sprockets (>= 3.0.0) sqlite3 (2.6.0-arm64-darwin) sqlite3 (2.6.0-x86_64-darwin) - sqlite3 (2.6.0-x86_64-linux-gnu) + sqlite3 (2.6.0-x86_64-linux) stringio (3.1.7) thor (1.3.2) timeout (0.4.3) @@ -210,9 +210,10 @@ GEM base64 websocket-extensions (>= 0.1.0) websocket-extensions (0.1.5) - zeitwerk (2.7.2) + zeitwerk (2.6.18) PLATFORMS + arm64-darwin-21 arm64-darwin-23 x86_64-darwin-22 x86_64-linux diff --git a/lib/atomic_admin/interaction.rb b/lib/atomic_admin/interaction.rb index 34fa975..7a30446 100644 --- a/lib/atomic_admin/interaction.rb +++ b/lib/atomic_admin/interaction.rb @@ -46,6 +46,9 @@ def resolve(**kwargs) schema = schema_factory.new(**kwargs) hash[:schema] = schema.schema hash[:uischema] = schema.uischema + when :launch + hash[:launch_url] = interaction[:launch_url] + hash[:aud] = interaction[:aud] end hash From 49b0c29797073c5550da41ffe76d3a73d2d0f6a1 Mon Sep 17 00:00:00 2001 From: Ryker Blunck Date: Fri, 24 Oct 2025 11:00:43 -0600 Subject: [PATCH 60/61] support launch url as a proc --- lib/atomic_admin/interaction.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/atomic_admin/interaction.rb b/lib/atomic_admin/interaction.rb index 7a30446..1ad1411 100644 --- a/lib/atomic_admin/interaction.rb +++ b/lib/atomic_admin/interaction.rb @@ -47,7 +47,7 @@ def resolve(**kwargs) hash[:schema] = schema.schema hash[:uischema] = schema.uischema when :launch - hash[:launch_url] = interaction[:launch_url] + hash[:launch_url] = interaction[:launch_url].call(**kwargs) hash[:aud] = interaction[:aud] end From 4822636ddc543cf982217e6ad503e549c02e07c5 Mon Sep 17 00:00:00 2001 From: Ryker Blunck Date: Fri, 31 Oct 2025 16:46:27 -0600 Subject: [PATCH 61/61] bump version --- lib/atomic_admin/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/atomic_admin/version.rb b/lib/atomic_admin/version.rb index bf3cbc6..e6d6b23 100644 --- a/lib/atomic_admin/version.rb +++ b/lib/atomic_admin/version.rb @@ -1,3 +1,3 @@ module AtomicAdmin - VERSION = "2.0.0.beta.4".freeze + VERSION = "2.0.0.beta.5".freeze end