Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move Users table to postgres #2555

Merged
merged 1 commit into from
Feb 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 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,14 @@ jobs:
- name: Initialize database
env:
RAILS_ENV: test
TEST_DATABASE_URL: ${{ steps.setup-postgres.outputs.db-url }}
TEST_MONGODB_URI: mongodb://localhost:27017/publisher_test
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
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
26 changes: 25 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,19 @@ 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
def assigned_to_id=(id)
self[:assigned_to_id] = id
end

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
Loading