diff options
-rw-r--r-- | app/controllers/uuid_resolver_controller.rb | 65 | ||||
-rw-r--r-- | app/helpers/map_helper.rb | 1 | ||||
-rw-r--r-- | app/helpers/uuid_resolver_helper.rb | 2 | ||||
-rw-r--r-- | app/javascript/scss/components/_footer.scss | 2 | ||||
-rw-r--r-- | app/jobs/ensure_agent_job.rb | 5 | ||||
-rw-r--r-- | app/lib/uuid_resolver.rb | 55 | ||||
-rw-r--r-- | app/models/user.rb | 4 | ||||
-rw-r--r-- | app/views/agents/_agent.html.erb | 2 | ||||
-rw-r--r-- | app/views/application/_user_info.html.erb | 5 | ||||
-rw-r--r-- | app/views/uuid_resolver/new.html.erb | 15 | ||||
-rw-r--r-- | app/views/welcome/dashboard.html.erb | 2 | ||||
-rw-r--r-- | config/database.yml | 2 | ||||
-rw-r--r-- | config/initializers/inflections.rb | 1 | ||||
-rw-r--r-- | config/routes.rb | 5 |
14 files changed, 158 insertions, 8 deletions
diff --git a/app/controllers/uuid_resolver_controller.rb b/app/controllers/uuid_resolver_controller.rb new file mode 100644 index 0000000..095d26d --- /dev/null +++ b/app/controllers/uuid_resolver_controller.rb @@ -0,0 +1,65 @@ +# == UUIDResolverController +# +# This controller enables applications to retrieve information about a given UUID. + +# It can be used to verify the availability of this UUID in the database, e.g., +# when a remote Agent assigns an UUID to a record, or when it looks up the +# record for that UUID. +# +# Usually it would either find no matching record (and can thus safely assign +# the UUID) or a single one (and be redirected to the matching resource.). +# +# It's also possible, but unlikely, that the UUID matches more than one record +# (e.g., a Map and a Resource), given the construction of UUIDs. In that case, +# the controller will return the list of matching records. +# +# === Verifying the availability of an UUID +# +# When the call is made to verify an UUID is not yet assigned, a 404 (Not Found) +# response means the UUID is available. +# +# === Identifying an existing record +# +# When the call is made to verify the existence of a record matching this UUID, +# a 302 (Found) response means a single record was found, and the Location +# header gives its URL. If a GET request was made, then the User-Agent will be +# redirected to that Location. +class UUIDResolverController < ApplicationController + # GET /by-uuid/:uuid + def new + @resolver = UUIDResolver.new(params[:uuid]) + + case @resolver.count + when 0 + render json: { + status: :not_found, + message: "UUID %<uuid>s is unknown to the system." % { uuid: @resolver.uuid }, + uuid: @resolver.uuid + }, + status: :not_found + when 1 + respond_to do |format| + format.html { redirect_to @resolver.record } + format.json { + render json: { + status: :found, + message: "UUID %<uuid>s was found at %<url>s." % { + uuid: @resolver.uuid, + url: url_for(@resolver.record) + }, + uuid: @resolver.uuid, + location: url_for(@resolver.record) + } + } + end + else + render json: { + status: :ok, + message: @resolver.records.to_json + }, + status: :ok + end + rescue ArgumentError => e + render plain: e.message, status: :unprocessable_entity + end +end diff --git a/app/helpers/map_helper.rb b/app/helpers/map_helper.rb index 3403801..8fadff8 100644 --- a/app/helpers/map_helper.rb +++ b/app/helpers/map_helper.rb @@ -4,6 +4,7 @@ module MapHelper def map_container(map = Map.first) + return unless map # Fixes a case where there are no maps raw tag.div( tag.div(id: 'map', data: { diff --git a/app/helpers/uuid_resolver_helper.rb b/app/helpers/uuid_resolver_helper.rb new file mode 100644 index 0000000..59f0aef --- /dev/null +++ b/app/helpers/uuid_resolver_helper.rb @@ -0,0 +1,2 @@ +module UUIDResolverHelper +end diff --git a/app/javascript/scss/components/_footer.scss b/app/javascript/scss/components/_footer.scss index 4ab34a2..4cc8046 100644 --- a/app/javascript/scss/components/_footer.scss +++ b/app/javascript/scss/components/_footer.scss @@ -1,5 +1,5 @@ body>footer { - position: absolute; + position: fixed; z-index: 1004; margin: 0 auto; background-color: rgba(0,0,0,0.5); diff --git a/app/jobs/ensure_agent_job.rb b/app/jobs/ensure_agent_job.rb index 127cf1d..a37b6fa 100644 --- a/app/jobs/ensure_agent_job.rb +++ b/app/jobs/ensure_agent_job.rb @@ -11,9 +11,12 @@ class EnsureAgentJob < ApplicationJob # Ensure the logged in user has a current agent # In order to do this, we first check the existing agents against the user's # groups. If none match, we assign the user to the default Anonymous agent. - existing_agents = Agent.find_by(name: groups) + existing_agents = Agent.where(name: groups) if existing_agents.nil? user.agents << default_agent unless user.agents.include? default_agent + else + # Update user agents + user.agents << existing_agents - user.agents end end end diff --git a/app/lib/uuid_resolver.rb b/app/lib/uuid_resolver.rb new file mode 100644 index 0000000..acca494 --- /dev/null +++ b/app/lib/uuid_resolver.rb @@ -0,0 +1,55 @@ +class UUIDResolver + # Note the static '4' in the third group: that's the UUID version. + UUID_V4_REGEX = %r[\A[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}\z] + + attr_reader :records, :count, :record, :uuid + + def initialize(uuid) + @uuid = validate!(uuid) + @records, @count = resolve! + + end + + def record + case @count + when 0 + nil + else + records.first + end + end + + private + + # List models that have UUIDs + def public_record_types + [ + ::Agent, + ::Map, + ::Resource, + ::Taxonomy + ].freeze + end + + # Find records with this UUID + def resolve! + records = [] + + public_record_types.each do |model| + records << model.find_by(uuid: @uuid) + end + + [records.compact, records.compact.size] + end + + # Ensure the passed UUID is correct + def validate!(uuid) + validate_uuid_v4(uuid) || raise(ArgumentError.new("You must pass a valid random UUID (https://tools.ietf.org/html/rfc4122)")) + end + + # Validate a UUID version 4 (random) + def validate_uuid_v4(uuid) + uuid = uuid.to_s.downcase + uuid.match?(UUID_V4_REGEX) ? uuid : false + end +end diff --git a/app/models/user.rb b/app/models/user.rb index 9e5c184..d45aa8b 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -9,7 +9,7 @@ class User < ApplicationRecord include UsersHelper def avatar_url - attributes[:avatar_url].present? ? - attributes[:avatar_url] : '' # default_user_avatar_url + attributes['avatar_url'].present? ? + attributes['avatar_url'] : '' # default_user_avatar_url end end diff --git a/app/views/agents/_agent.html.erb b/app/views/agents/_agent.html.erb index fda8bd0..aaeb9b0 100644 --- a/app/views/agents/_agent.html.erb +++ b/app/views/agents/_agent.html.erb @@ -1,7 +1,7 @@ <article class="agent" id="agent-<%= agent.to_param %>"> <header> <h1><%= link_to agent.name, agent_url(agent) %></h1> - <h2><%= link_to agent_url(agent) %></h1> + <h2><%= link_to agent_url(agent), agent_url(agent) %></h1> </head> <% if current_agent == agent %> <section class="agent-info"> diff --git a/app/views/application/_user_info.html.erb b/app/views/application/_user_info.html.erb index 048e6fb..0f003bb 100644 --- a/app/views/application/_user_info.html.erb +++ b/app/views/application/_user_info.html.erb @@ -11,3 +11,8 @@ <% end %> </nav> </section> + +<% content_for :debug do %> +<%= @current_user&.avatar_url %> +<% end %> + diff --git a/app/views/uuid_resolver/new.html.erb b/app/views/uuid_resolver/new.html.erb new file mode 100644 index 0000000..1cefbfc --- /dev/null +++ b/app/views/uuid_resolver/new.html.erb @@ -0,0 +1,15 @@ +<h1>Resolving <%= @resolver.uuid %></h1> +<p>Found <%= pluralize(@resolver.count, "result") %>.</p> + +<% if @resolver.count == 1 %> + <%= render @resolver.record %> +<% elsif @resolver.count > 1 %> + <% @resolver.records.each do |rec| %> + <%= render rec %> + <% end %> +<% end %> + +<% content_for :debug do %> + <%= h @resolver.records.inspect %> +<% end %> + diff --git a/app/views/welcome/dashboard.html.erb b/app/views/welcome/dashboard.html.erb index 242fc58..42b827a 100644 --- a/app/views/welcome/dashboard.html.erb +++ b/app/views/welcome/dashboard.html.erb @@ -4,7 +4,7 @@ <p><%= form_for current_agent, url: '/my/current_agent', method: :patch do |f| %> En cours : <%= f.submit('select') %> - <%= f.select(:id, options_for_select(current_user.agents.map { |a| [a.name, a.id] }), selected: current_agent.id) %> + <%= f.select(:id, options_for_select(current_user.agents.map { |a| [a.name, a.id] }, current_agent.id)) %> <% end %></p> diff --git a/config/database.yml b/config/database.yml index 1912468..16ba3d2 100644 --- a/config/database.yml +++ b/config/database.yml @@ -24,7 +24,7 @@ default: &default # For details on connection pooling, see Rails configuration guide # https://guides.rubyonrails.org/configuring.html#database-pooling pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> - host: <%= ENV.fetch("DATABASE_HOST", "db") %> + host: <%= ENV.fetch("DATABASE_HOST", "localhost") %> database: <%= ENV.fetch("POSTGRES_DB", "the_map_db") %> username: <%= ENV.fetch("POSTGRES_USER", "postgres") %> password: <%= ENV.fetch("POSTGRES_PASSWORD", "postgres") %> diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb index 2292fab..45c08f4 100644 --- a/config/initializers/inflections.rb +++ b/config/initializers/inflections.rb @@ -24,4 +24,5 @@ ActiveSupport::Inflector.inflections do |inflect| inflect.acronym 'API' inflect.acronym 'SSO' inflect.acronym 'INCOMMON' + inflect.acronym 'UUID' end diff --git a/config/routes.rb b/config/routes.rb index 9be4a4e..54f383e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -28,7 +28,10 @@ Rails.application.routes.draw do get '/my/account', to: 'users#show', as: 'account' patch '/my/current_agent', to: 'my/agent#switch', as: 'agent_switch' get '/my/dashboard', to: 'welcome#dashboard' - get 'my/peers', to: 'users#index', as: 'users' + get '/my/peers', to: 'users#index', as: 'users' + + # UUID Resolver + get '/by-uuid/:uuid', to: 'uuid_resolver#new', as: 'uuid_resolver' # Discourse SSO get 'authenticate(/:token)', to: 'welcome#authenticate' |