aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/controllers/uuid_resolver_controller.rb65
-rw-r--r--app/helpers/map_helper.rb1
-rw-r--r--app/helpers/uuid_resolver_helper.rb2
-rw-r--r--app/javascript/scss/components/_footer.scss2
-rw-r--r--app/jobs/ensure_agent_job.rb5
-rw-r--r--app/lib/uuid_resolver.rb55
-rw-r--r--app/models/user.rb4
-rw-r--r--app/views/agents/_agent.html.erb2
-rw-r--r--app/views/application/_user_info.html.erb5
-rw-r--r--app/views/uuid_resolver/new.html.erb15
-rw-r--r--app/views/welcome/dashboard.html.erb2
-rw-r--r--config/database.yml2
-rw-r--r--config/initializers/inflections.rb1
-rw-r--r--config/routes.rb5
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'