Skip to content

Commit

Permalink
Move Users table to postgres
Browse files Browse the repository at this point in the history
  • Loading branch information
syed-ali-tw committed Feb 25, 2025
1 parent 7b2874a commit f9343a3
Show file tree
Hide file tree
Showing 27 changed files with 235 additions and 83 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/minitest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ jobs:
- name: Setup Postgres
id: setup-postgres
uses: alphagov/govuk-infrastructure/.github/actions/setup-postgres@main
with:
POSTGRES_IMAGE_TAG: 16
POSTGRES_DB: publisher_test

- name: Setup Redis
uses: alphagov/govuk-infrastructure/.github/actions/setup-redis@main
Expand Down Expand Up @@ -57,10 +60,13 @@ jobs:
- name: Initialize database
env:
RAILS_ENV: test
TEST_DATABASE_URL: ${{ steps.setup-postgres.outputs.db-url }}
run: bundle exec rails db:setup

- name: Run Minitest
env:
RAILS_ENV: test
TEST_DATABASE_URL: ${{ steps.setup-postgres.outputs.db-url }}
TEST_MONGODB_URI: mongodb://localhost:27017/publisher_test
GOVUK_CONTENT_SCHEMAS_PATH: vendor/publishing-api/content_schemas
run: bundle exec rake test
8 changes: 3 additions & 5 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ gem "mongoid-sadstory"
gem "mousetrap-rails"
gem "nested_form", git: "https://github.com/alphagov/nested_form.git", branch: "add-wrapper-class"
gem "null_logger"
gem "pg"
gem "plek"
gem "prometheus-client"
gem "rails_autolink"
Expand All @@ -41,20 +42,17 @@ gem "sentry-sidekiq"
gem "sidekiq", "< 8" # Disables Sidekiq 7 beta opt-in.
gem "sprockets-rails"
gem "state_machines"
gem "state_machines-activerecord"
gem "state_machines-mongoid"
gem "strip_attributes"
gem "terser"
gem "whenever", require: false

# postgres dependencies
gem "pg"
gem "state_machines-activerecord"
gem "database_cleaner-active_record"

group :test do
gem "capybara-select-2"
gem "ci_reporter_minitest"
gem "climate_control"
gem "database_cleaner-active_record"
gem "database_cleaner-mongoid"
gem "factory_bot_rails"
gem "govuk_schemas"
Expand Down
2 changes: 1 addition & 1 deletion app/helpers/publications_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def content_tagger_url(edition)
end

def enabled_users_select_options
options = User.enabled.order_by([%i[name asc]]).collect { |u| [u.name, u.id] }
options = User.enabled.order([:name]).collect { |u| [u.name, u.id] }
options.unshift(["", ""])
end
end
2 changes: 1 addition & 1 deletion app/helpers/tabbed_nav_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ def available_assignee_items(edition)
items << { value: "none", text: "None" }
items << :or
end
User.enabled.order_by([%i[name asc]]).each do |user|
User.enabled.order([:name]).each do |user|
items << { value: user.id, text: user.name } unless user.name == edition.assignee || !user.has_editor_permissions?(edition)
end
items
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ module GovukContentModels
module ActionProcessors
class AssignProcessor < BaseProcessor
def process
edition.set(assigned_to_id: action_attributes[:recipient_id])
# rubocop:disable Rails/SaveBang
edition.update(assigned_to_id: action_attributes[:recipient_id])
edition.reload
# rubocop:enable Rails/SaveBang
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion app/lib/tagging/tagging_update_form.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class TaggingUpdateForm
validate :ordered_related_items_paths_exist

def self.build_from_publishing_api(content_id, locale)
link_set = LinkSet.find(content_id, locale)
link_set = Tagging::LinkSet.find(content_id, locale)

new(
content_id:,
Expand Down
38 changes: 36 additions & 2 deletions app/models/action.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,13 @@ class Action

embedded_in :edition

belongs_to :recipient, class_name: "User", optional: true
belongs_to :requester, class_name: "User", optional: true
# Temp-to-be-brought-back
# Currently we are using recipient_id & requester_id as a field to store the id's
# to bypass the issue of having a belongs_to between a postgres table and a mongo table
# we will most likely bring back the belongs_to relationship once we move action table to postgres.

# belongs_to :recipient, class_name: "User", optional: true
# belongs_to :requester, class_name: "User", optional: true

field :approver_id, type: Integer
field :approved, type: DateTime
Expand All @@ -43,6 +48,12 @@ class Action
field :customised_message, type: String
field :created_at, type: DateTime, default: -> { Time.zone.now }

# Temp-to-be-removed
# This will be removed once we move action table to postgres, this temporarily
# allows to support the belongs to relation between action and user
field :recipient_id, type: BSON::ObjectId
field :requester_id, type: BSON::ObjectId

def container_class_name(edition)
edition.container.class.name.underscore.humanize
end
Expand All @@ -65,4 +76,27 @@ def is_fact_check_request?
# SEND_FACT_CHECK is now a state - in older publications it isn't
[SEND_FACT_CHECK, "fact_check_requested"].include?(request_type)
end

# Temp-to-be-removed
# The method below are getters and setters for assigned_to that allows us to set the requester & requester_id and get recipient & recipient_id.
# We are unable to use [belongs_to :recipient, class_name: "User", optional: true & belongs_to :requester, class_name: "User", optional: true] as the User table is
# in postgres and using a combination of setter and getter methods with a recipient_id & requester_id field
# to be able to achieve congruent result as having a belongs to while we are moving other table to postgres
def recipient
User.find(recipient_id) if recipient_id
end

def requester
User.find(requester_id) if requester_id
rescue StandardError
nil
end

def recipient=(user)
self.recipient_id = user.id
end

def requester=(user)
self.requester_id = user.id
end
end
3 changes: 3 additions & 0 deletions app/models/application_record.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
end
13 changes: 13 additions & 0 deletions app/models/artefact.rb
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,12 @@ def record_action(action_type, options = {})
attributes[:user] = user if user
attributes[:task_performed_by] = task_name if task_name

# Temp-to-be-removed
# This will be removed once we move artefact table to postgres, currently record_action
# when called with options contains the user object attributes that is supported by belongs to
# the below code allows to use the user id foreign key instead as we are temporarily not using belongs to
# relationship between artefact and user
add_user_id_and_delete_user_key(attributes)
new_action = actions.build(attributes)
# Mongoid will not fire creation callbacks on embedded documents, so we
# need to trigger this manually. There is a `cascade_callbacks` option on
Expand All @@ -225,6 +231,13 @@ def record_action(action_type, options = {})
end
end

def add_user_id_and_delete_user_key(attributes)
if attributes[:user]
attributes[:user_id] = attributes[:user].id
attributes.delete(:user)
end
end

def archived?
state == "archived"
end
Expand Down
17 changes: 16 additions & 1 deletion app/models/artefact_action.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,27 @@ class ArtefactAction
field "snapshot", type: Hash
field "task_performed_by", type: String

# Temp-to-be-removed
# This will be removed once we move artefact_action table to postgres, this temporarily
# allows to support the belongs_to relation between artefact_action and user
field "user_id", type: BSON::ObjectId

embedded_in :artefact

# Ideally we would like to use the UID field here, since that will be the
# same across all applications, but Mongoid doesn't yet support using a
# custom primary key on a related field
belongs_to :user, optional: true

# Temp-to-be-brought-back
# Currently we are using user_id as a field to store the user_id
# to bypass the issue of having a belongs_to between a postgres table and a mongo table
# we will most likely bring back the belongs_to relationship once we move artefact_action table to postgres.

# belongs_to :user, optional: true

def user
User.find(user_id) if user_id
end

# Not validating presence of a user just yet, since there may be some
# circumstances where we can't reliably determine the user. As an example
Expand Down
30 changes: 29 additions & 1 deletion app/models/edition.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,21 @@ class ResurrectionError < RuntimeError
field :change_note, type: String
field :review_requested_at, type: DateTime

# Temp-to-be-removed
# This will be removed once we move edition table to postgres, this temporarily
# allows to support the belongs_to relation between edition and user
field :assigned_to_id, type: BSON::ObjectId

field :auth_bypass_id, type: String, default: -> { SecureRandom.uuid }

field :owning_org_content_ids, type: Array, default: []

belongs_to :assigned_to, class_name: "User", optional: true
# Temp-to-be-brought-back
# Currently we are using assigned_to_id as a field to store the assigned_to_id
# to bypass the issue of having a belongs_to between a postgres table and a mongo table
# we will most likely bring back the belongs_to relationship once we move edition table to postgres.

# belongs_to :assigned_to, class_name: "User", optional: true

embeds_many :link_check_reports

Expand Down Expand Up @@ -521,6 +531,24 @@ def is_accessible_to?(user)
owning_org_content_ids.include?(user.organisation_content_id)
end

# Temp-to-be-removed
# The method below are getters and setters for assigned_to that allows us to set the assigned_to_id and get assigned_to.
# We are unable to use belongs_to :assigned_to, class_name: "User as the User table is
# in postgres and using a combination of setter and getter methods with a assigned_to_id field
# to be able to achieve congruent result as having a belongs to while we are moving other table to postgres

def assigned_to_id=(id)
self[:assigned_to_id] = id
end

def assigned_to=(user)
self[:assigned_to_id] = user.id
end

def assigned_to
User.find(assigned_to_id) if assigned_to_id
end

private

def base_field_keys
Expand Down
35 changes: 3 additions & 32 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,40 +3,11 @@
require "gds-sso/user"
require_dependency "safe_html"

class User
include Mongoid::Document
include Mongoid::Timestamps
class User < ApplicationRecord
include GDS::SSO::User

# Let an app configure the symbolized collection name to use,
# e.g. set a constant in an initializer.
if defined?(USER_COLLECTION_NAME)
store_in collection: USER_COLLECTION_NAME.to_sym
else
store_in collection: :users
end

field "name", type: String
field "uid", type: String
field "version", type: Integer
field "email", type: String
field "permissions", type: Array, default: []
field "remotely_signed_out", type: Boolean, default: false
field "organisation_slug", type: String
field "disabled", type: Boolean, default: false
field "organisation_content_id", type: String

index({ uid: 1 }, unique: true)
index disabled: 1

scope :alphabetized, -> { order_by(name: :asc) }
scope :enabled,
lambda {
any_of(
{ :disabled.exists => false },
{ :disabled.in => [false, nil] },
)
}
scope :alphabetized, -> { order(name: :asc) }
scope :enabled, -> { where("disabled IS NULL OR disabled = ?", false) }

def to_s
name || email || ""
Expand Down
24 changes: 23 additions & 1 deletion app/models/workflow.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,18 @@ class CannotDeletePublishedPublication < RuntimeError; end
after_create :notify_siblings_of_new_edition

field :state, type: String, default: "draft"
belongs_to :assigned_to, class_name: "User", optional: true

# Temp-to-be-removed
# This will be removed once we move workflow module to support postgres models, this temporarily
# allows to support the belongs_to relation between workflow and user
field :assigned_to_id, type: BSON::ObjectId

# Temp-to-be-brought-back
# Currently we are using assigned_to_id as a field to store the assigned_to_id
# to bypass the issue of having a belongs_to between a postgres table and a mongo table
# we will most likely bring back the belongs_to relationship once we move workflow to support postgres.

# belongs_to :assigned_to, class_name: "User", optional: true

state_machine initial: :draft do
after_transition on: :request_amendments do |edition, _transition|
Expand Down Expand Up @@ -191,6 +202,17 @@ def important_note
action if action.try(:request_type) == Action::IMPORTANT_NOTE
end

# Temp-to-be-removed
# The method below are getters and setters for assigned_to that allows us to set the assigned_to_id and get assigned_to.
# We are unable to use belongs_to :assigned_to, class_name: "User as the User table is
# in postgres and using a combination of setter and getter methods with a assigned_to_id field
# to be able to achieve congruent result as having a belongs to while we are moving other table to postgres
delegate :assigned_to_id=, to: self

def assigned_to
User.find(assigned_to_id) if assigned_to_id
end

private

def notify_siblings_of_published_edition
Expand Down
4 changes: 2 additions & 2 deletions app/presenters/filtered_editions_presenter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,9 @@ def apply_assigned_to_filter(editions)
editions = editions.assigned_to(nil)
else
begin
assigned_user = User.find(assigned_to_filter)
assigned_user = User.find(assigned_to_filter) if assigned_to_filter.present?
editions = editions.assigned_to(assigned_user) if assigned_user
rescue Mongoid::Errors::DocumentNotFound
rescue ActiveRecord::RecordNotFound
Rails.logger.warn "An attempt was made to filter by an unknown user ID: '#{assigned_to_filter}'"
end
end
Expand Down
2 changes: 1 addition & 1 deletion app/views/legacy_editions/_reviewer_field.html.erb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
<%= form_group(f, :reviewer, label: "Reviewer") do %>
<%= f.select :reviewer, User.enabled.order_by([[:name, :asc]]).collect{ |p| [p.name, p.name] }, { :include_blank => true }, { :class => "form-control input-md-3", :disabled => @resource.locked_for_edits?, "data-module" => "assignee-select"} %>
<%= f.select :reviewer, User.enabled.order([:name]).collect{ |p| [p.name, p.name] }, { :include_blank => true }, { :class => "form-control input-md-3", :disabled => @resource.locked_for_edits?, "data-module" => "assignee-select"} %>
<% end %>
3 changes: 2 additions & 1 deletion config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
require "rails"
# Pick the frameworks you want:
require "active_model/railtie"
require "active_record/railtie"
require "active_job/railtie"
require "action_controller/railtie"
require "action_mailer/railtie"
Expand Down Expand Up @@ -67,7 +68,7 @@ class Application < Rails::Application
}

config.generators do |g|
g.orm :mongoid
g.orm :active_record
g.template_engine :erb # this could be :haml or whatever
g.test_framework :test_unit, fixture: false # this could be :rpsec or whatever
end
Expand Down
Loading

0 comments on commit f9343a3

Please sign in to comment.