diff --git a/backend/app/controllers/tags_controller.rb b/backend/app/controllers/tags_controller.rb index d60d56d..13b71ba 100644 --- a/backend/app/controllers/tags_controller.rb +++ b/backend/app/controllers/tags_controller.rb @@ -770,31 +770,15 @@ class TagsController < ApplicationController TagVersioning.ensure_snapshot!(old_owner_tag, created_by_user: current_user) end - current_tag_name.wiki_page&.update!(tag_name_id: promoted_tag_name.id) - current_tag_name.association(:wiki_page).reset - - current_tag_name.update!(canonical_id: promoted_tag_name.id, - primary_flg: false, auto_generated: false) - promoted_tag_name.update!(tag:, canonical_id: nil, primary_flg: true, auto_generated: false) - - TagName.where(tag:, - language_code: current_tag_name.language_code, - primary_flg: false) - .where.not(id: promoted_tag_name.id) - .find_each do |alias_tag_name| - alias_tag_name.update!(canonical_id: promoted_tag_name.id, primary_flg: false) - end - - tag.update!(tag_name_id: promoted_tag_name.id) - current_tag_name.association(:wiki_page).reset - current_tag_name.association(:tag).reset - current_tag_name.reload.update!(canonical_id: promoted_tag_name.id, primary_flg: false) + current_tag_name&.update!(primary_flg: false, auto_generated: false) + promoted_tag_name.update!(tag:, primary_flg: true, auto_generated: false) + tag.association(:tag_names).reset return unless old_owner_tag && old_owner_tag != tag record_tag_version!(old_owner_tag.reload, event_type: :update, created_by_user: current_user, - language_code: current_tag_name.language_code) + language_code: promoted_tag_name.language_code) end def update_aliases! locale, tag, alias_names @@ -802,20 +786,15 @@ class TagsController < ApplicationController affected_tags = [tag] - primary_tag_name = TagName.find_by!(tag:, - language_code: locale.language_code, - primary_flg: true) current_aliases = tag.tag_names.where(language_code: locale.language_code, primary_flg: false).to_a - current_aliases.each do |alias_tag_name| - next if alias_names.include?(alias_tag_name.name) - - affected_tags << alias_tag_name.tag - end + requested_aliases = TagName.where( + language_code: locale.language_code, name: alias_names) + .includes(:tag).index_by(&:name) alias_names.each do |alias_name| - alias_tag_name = TagName.find_by(language_code: locale.language_code, name: alias_name) + alias_tag_name = requested_aliases[alias_name] if alias_tag_name&.primary? && alias_tag_name.tag_id && alias_tag_name.tag_id != tag.id alias_tag_name.errors.add(:name, 'その名前は既に使はれてゐます.') raise ActiveRecord::RecordInvalid, alias_tag_name @@ -831,16 +810,15 @@ class TagsController < ApplicationController current_aliases.each do |alias_tag_name| next if alias_names.include?(alias_tag_name.name) - alias_tag_name.update!(canonical_id: nil, tag_id: nil) + alias_tag_name.destroy! end alias_names.each do |alias_name| - alias_tag_name = TagName.find_or_initialize_by( + alias_tag_name = requested_aliases[alias_name] || TagName.new( language_code: locale.language_code, name: alias_name) alias_tag_name.script_code = locale.script_code - alias_tag_name.update!(tag:, primary_flg: false, auto_generated: false, - canonical_id: primary_tag_name.id) + alias_tag_name.update!(tag:, primary_flg: false, auto_generated: false) end affected_tags.compact.uniq.each do |affected_tag| diff --git a/backend/app/controllers/wiki_pages_controller.rb b/backend/app/controllers/wiki_pages_controller.rb index 797c088..688fe7b 100644 --- a/backend/app/controllers/wiki_pages_controller.rb +++ b/backend/app/controllers/wiki_pages_controller.rb @@ -1,7 +1,7 @@ class WikiPagesController < ApplicationController # 互換性のための Facade API - rescue_from Wiki::Commit::Conflict, with: :render_wiki_conflict + rescue_from TagDescriptionOperation::Conflict, with: :render_wiki_conflict def index title = params[:title].to_s.strip @@ -38,7 +38,7 @@ class WikiPagesController < ApplicationController tag_group = TagGroup.find_by(id: params[:id]) return render_tag_group tag_group if tag_group - render_wiki_page_or_404 nil + head :not_found end def show_by_title @@ -55,7 +55,7 @@ class WikiPagesController < ApplicationController return render_wiki_page_by_tag_description tag_description if tag_description - render_wiki_page_or_404 nil + head :not_found end def exists @@ -134,15 +134,15 @@ class WikiPagesController < ApplicationController if title.start_with?(TagGroupRepr::PREFIX) name = title.delete_prefix(TagGroupRepr::PREFIX) parsed = TagGroupMarkdownParser.parse(body:, group: nil) - group = TagGroupOperation.create!(name:, **parsed, actor: current_user, - require_compatibility: true) + group = TagGroupOperation.create!( + name:, **parsed.except(:compatibility_body), actor: current_user) version = current_group_version(group) return render_group_representation(TagGroupRepr.one(group), version, body: parsed.fetch(:compatibility_body), status: :created) end - page = + description = if title.include?(':') return render_unprocessable_entity field: :title else @@ -153,7 +153,6 @@ class WikiPagesController < ApplicationController tag: tag_name.tag, body:, created_by_user: current_user, message:) end - description = TagDescription.find_by!(tag_id: page.tag_name.tag_id, language_code: 'ja') render json: WikiPageRepr.tag_description(description), status: :created rescue ActiveRecord::RecordInvalid => e render_validation_error e.record @@ -179,22 +178,21 @@ class WikiPagesController < ApplicationController name = title.delete_prefix(TagGroupRepr::PREFIX) parsed = TagGroupMarkdownParser.parse(body:, group:) - TagGroupOperation.update!(group:, name:, **parsed, actor: current_user, - require_compatibility: true) + TagGroupOperation.update!( + group:, name:, **parsed.except(:compatibility_body), actor: current_user) return head :ok end return render_unprocessable_entity field: :title if title.include?(':') description = TagDescription.find_by(id: params[:id]) unless description - return render_unprocessable_entity field: :title if group || WikiPage.exists?(id: params[:id]) + return render_unprocessable_entity field: :title if group return head :not_found end tag = description.tag - tag_name = TagDescriptionOperation.compatibility_tag_name(tag) - if tag_name.name.include?(':') || (group && title != tag_name.name) - return render_unprocessable_entity field: :title - end + tag_name = tag.tag_name('ja') + return render_unprocessable_entity field: :title unless tag_name + return render_unprocessable_entity field: :title if tag_name.name.include?(':') base_revision_id = params[:base_revision_id].presence @@ -251,26 +249,6 @@ class WikiPagesController < ApplicationController private - def legacy_group_page? page - return false unless page - return true if page.title.start_with?(TagGroupRepr::PREFIX) - - prefix = ApplicationRecord.sanitize_sql_like(TagGroupRepr::PREFIX) - page.wiki_versions.where('title LIKE ?', "#{ prefix }%").exists? - end - - def legacy_update_identified? page, title, group - return false unless page - return true if page.title == title - return false if title == group.name - - namespace = page.title.split(':', 2) - return true if namespace.size == 2 && title.start_with?("#{ namespace.first }:") - - base = params[:base_revision_id].presence - base && page.wiki_revisions.exists?(id: base) - end - def render_tag_group group version = if params[:version].present? group_version(group, params[:version]) @@ -357,27 +335,6 @@ class WikiPagesController < ApplicationController end end - def render_wiki_page_or_404 page - return head :not_found unless page - - rev = find_revision(page) - return head :not_found unless rev - - if rev.redirect? - return ( - redirect_to wiki_page_by_title_path(title: rev.redirect_page.title), - status: :moved_permanently) - end - - body = rev.body - revision_id = rev.id - pred = page.pred_revision_id(revision_id) - succ = page.succ_revision_id(revision_id) - updated_at = rev.created_at - - render json: WikiPageRepr.base(page).merge(body:, revision_id:, pred:, succ:, updated_at:) - end - def render_wiki_page_by_tag_description description versions = description_versions(description) historical = params[:version].present? @@ -432,14 +389,6 @@ class WikiPagesController < ApplicationController end end - def find_revision page - if params[:version].present? - page.wiki_revisions.find_by(id: params[:version]) - else - page.current_revision - end - end - def render_wiki_conflict err render json: { error: 'conflict', message: err.message }, status: :conflict end diff --git a/backend/app/models/locale.rb b/backend/app/models/locale.rb index 1d39f44..b06fab9 100644 --- a/backend/app/models/locale.rb +++ b/backend/app/models/locale.rb @@ -13,14 +13,13 @@ class Locale < ApplicationRecord def generate_tag_names! ApplicationRecord.transaction do tag_ids = TagName.where(language_code:, primary_flg: true).pluck(:tag_id) - Tag.where.not(id: tag_ids).find_each do + Tag.where.not(id: tag_ids).includes(:tag_names).find_each do TagName.create!(tag_id: _1.id, language_code:, name: TagName.generate_name(self, _1, _1.tag_name('ja')), script_code:, primary_flg: true, - auto_generated: true, - canonical_id: nil) + auto_generated: true) end end end diff --git a/backend/app/models/tag.rb b/backend/app/models/tag.rb index 173a52a..9baa7e6 100644 --- a/backend/app/models/tag.rb +++ b/backend/app/models/tag.rb @@ -91,7 +91,6 @@ class Tag < ApplicationRecord end end def name(language_code) = tag_name(language_code)&.name - def wiki_page = tag_name('ja')&.wiki_page def system? tag_name('ja')&.name.in?(SYSTEM_TAG_NAMES) @@ -260,16 +259,13 @@ class Tag < ApplicationRecord name = name.to_s.strip ApplicationRecord.transaction do - tn = TagName.find_or_create_by!(language_code:, name:) do - _1.script_code = locale.script_code - _1.primary_flg = true - end + tn = TagName.find_by(language_code:, name:) + next tn.tag if tn - tag = tn.tag - next tag if tag - - tag = Tag.create!(tag_name_id: tn.id, category:) - tn.update!(tag:, primary_flg: true, canonical_id: nil, auto_generated: false) + tag = Tag.create!(category:) + tn = TagName.create!(tag:, language_code:, name:, + script_code: locale.script_code, primary_flg: true, + auto_generated: false) generate_tag_names!(locale, tag, tn) tag.association(:tag_names).reset @@ -303,20 +299,17 @@ class Tag < ApplicationRecord end end - source_tag_name = source_tag.tag_name('ja') - - if source_tag_name.wiki_page.present? - raise ActiveRecord::RecordInvalid.new(source_tag_name) + if source_tag.tag_descriptions.exists? + source_tag.errors.add(:base, :invalid) + raise ActiveRecord::RecordInvalid, source_tag end TagVersioning.record!(source_tag, event_type: :discard, created_by_user:) source_tag.tag_names.update_all(tag_id: target_tag.id, primary_flg: false, + auto_generated: false, updated_at: Time.current) source_tag.destroy! - source_tag_name.update_columns(canonical_id: target_tag.tag_name_id, - updated_at: Time.current) - TagVersioning.record!(target_tag, event_type: :update, created_by_user:) end @@ -376,8 +369,7 @@ class Tag < ApplicationRecord name: TagName.generate_name(locale, tag, base_tag_name), script_code:, primary_flg: true, - auto_generated: true, - canonical_id: nil) + auto_generated: true) end end end diff --git a/backend/app/models/tag_name.rb b/backend/app/models/tag_name.rb index 6c119ab..05b6b92 100644 --- a/backend/app/models/tag_name.rb +++ b/backend/app/models/tag_name.rb @@ -1,14 +1,12 @@ class TagName < ApplicationRecord belongs_to :language, foreign_key: :language_code, primary_key: :code - belongs_to :tag, optional: true - has_one :wiki_page + belongs_to :tag validates :name, presence: true, length: { maximum: 255 }, uniqueness: { scope: :language_code } validate :alias_name_must_not_have_prefix - validate :alias_must_not_have_wiki_page validate :name_must_be_sanitised validate :name_mustnt_start_with_nico validate :generated_name_must_be_reserved @@ -21,14 +19,16 @@ class TagName < ApplicationRecord tns = TagName.where(language_code: locale.language_code, name: names).index_by(&:name) + primary_names = TagName.where( + language_code: locale.language_code, tag_id: tns.values.map(&:tag_id), + primary_flg: true).pluck(:tag_id, :name).to_h + names.map { |name| tag_name = tns[name] - if !tag_name || tag_name.primary? || tag_name.tag_id.nil? + if !tag_name || tag_name.primary? name else - TagName.find_by(language_code: locale.language_code, - tag_id: tag_name.tag_id, - primary_flg: true)&.name || name + primary_names[tag_name.tag_id] || name end }.uniq end @@ -53,12 +53,6 @@ class TagName < ApplicationRecord end end - def alias_must_not_have_wiki_page - if !(primary?) && wiki_page - errors.add :primary_flg, 'Wiki 参照がある名前はエーリアスになれません.' - end - end - def name_must_be_sanitised if name? && name != TagNameSanitisationRule.sanitise(name) errors.add :name, '名前に使用できない文字が含まれてゐます.' diff --git a/backend/app/models/tag_name_sanitisation_rule.rb b/backend/app/models/tag_name_sanitisation_rule.rb index 4731d4f..b853f00 100644 --- a/backend/app/models/tag_name_sanitisation_rule.rb +++ b/backend/app/models/tag_name_sanitisation_rule.rb @@ -33,13 +33,7 @@ class TagNameSanitisationRule < ApplicationRecord existing_tag = existing_tn.tag source_tag = tn.tag - if existing_tag - Tag.merge_tags!(existing_tag, source_tag) if tn.tag - elsif source_tag - tn.update_columns(tag_id: nil, updated_at: Time.current) - source_tag.update_columns(tag_name_id: existing_tn.id, updated_at: Time.current) - existing_tn.update_columns(tag_id: source_tag.id, updated_at: Time.current) - end + Tag.merge_tags!(existing_tag, source_tag) if existing_tag != source_tag tn.destroy! next diff --git a/backend/app/models/user.rb b/backend/app/models/user.rb index 59b076c..3b0b87b 100644 --- a/backend/app/models/user.rb +++ b/backend/app/models/user.rb @@ -13,10 +13,10 @@ class User < ApplicationRecord has_many :ip_addresses, through: :user_ips has_many :user_post_views, dependent: :destroy has_many :viewed_posts, through: :user_post_views, source: :post - has_many :created_wiki_pages, - class_name: 'WikiPage', foreign_key: :created_user_id, dependent: :nullify - has_many :updated_wiki_pages, - class_name: 'WikiPage', foreign_key: :updated_user_id, dependent: :nullify + has_many :created_tag_descriptions, class_name: 'TagDescription', + foreign_key: :created_by_user_id, dependent: :nullify + has_many :updated_tag_descriptions, class_name: 'TagDescription', + foreign_key: :updated_by_user_id, dependent: :nullify def viewed?(post) = user_post_views.exists?(post_id: post.id) diff --git a/backend/app/models/wiki_line.rb b/backend/app/models/wiki_line.rb deleted file mode 100644 index b9c3465..0000000 --- a/backend/app/models/wiki_line.rb +++ /dev/null @@ -1,15 +0,0 @@ -class WikiLine < ApplicationRecord - has_many :wiki_revision_lines, dependent: :restrict_with_exception - - validates :sha256, presence: true, uniqueness: true, length: { is: 64 } - validates :body, presence: true - - def self.upsert_by_body! body - sha = Digest::SHA256.hexdigest(body) - now = Time.current - - upsert(sha256: sha, body:, created_at: now, updated_at: now) - - find_by!(sha256: sha) - end -end diff --git a/backend/app/models/wiki_page.rb b/backend/app/models/wiki_page.rb deleted file mode 100644 index caee630..0000000 --- a/backend/app/models/wiki_page.rb +++ /dev/null @@ -1,58 +0,0 @@ -require 'set' - - -class WikiPage < ApplicationRecord - include MyDiscard - - has_many :wiki_revisions, dependent: :destroy - belongs_to :created_user, class_name: 'User' - belongs_to :updated_user, class_name: 'User' - - has_many :redirected_from_revisions, - class_name: 'WikiRevision', - foreign_key: :redirect_page_id, - dependent: :nullify - - has_many :wiki_versions - - attribute :version_no, :integer, default: 1 - - belongs_to :tag_name - validates :tag_name, presence: true - validates :body, presence: true - - def title = tag_name.name - def deprecated_at = tag_name.tag&.deprecated_at - - def title= val - (self.tag_name ||= build_tag_name).name = val - end - - def current_revision = wiki_revisions.order(id: :desc).first - - def resolve_redirect limit: 10 - page = self - visited = Set.new - - limit.times do - return page if visited.include?(page.id) - - visited.add(page.id) - - rev = page.current_revision - return page if !(rev&.redirect?) || !(rev.redirect_page) - - page = rev.redirect_page - end - - page - end - - def pred_revision_id revision_id - wiki_revisions.where('id < ?', revision_id).order(id: :desc).limit(1).pick(:id) - end - - def succ_revision_id revision_id - wiki_revisions.where('id > ?', revision_id).order(id: :asc).limit(1).pick(:id) - end -end diff --git a/backend/app/models/wiki_revision.rb b/backend/app/models/wiki_revision.rb deleted file mode 100644 index 3e0781a..0000000 --- a/backend/app/models/wiki_revision.rb +++ /dev/null @@ -1,55 +0,0 @@ -class WikiRevision < ApplicationRecord - belongs_to :wiki_page - belongs_to :base_revision, class_name: 'WikiRevision', optional: true - belongs_to :created_user, class_name: 'User' - belongs_to :redirect_page, class_name: 'WikiPage', optional: true - - has_many :wiki_revision_lines, dependent: :delete_all - has_many :wiki_lines, through: :wiki_revision_lines - - enum :kind, content: 0, redirect: 1 - - validates :kind, presence: true - validates :lines_count, numericality: { only_integer: true, greater_than_or_equal_to: 0 } - validates :tree_sha256, length: { is: 64 }, allow_nil: true - - validate :kind_consistency - - def body - return unless content? - - wiki_revision_lines - .includes(:wiki_line) - .order(:position) - .map { |rev| rev.wiki_line.body } - .join("\n") - end - - private - - def kind_consistency - if content? - if tree_sha256.blank? - errors.add(:tree_sha256, '種類がページの場合は必須です.') - end - - if redirect_page_id.present? - errors.add(:redirect_page_id, '種類がページの場合は空である必要があります.') - end - end - - if redirect? - if redirect_page_id.blank? - errors.add(:redirect_page_id, '種類がリダイレクトの場合は必須です.') - end - - if tree_sha256.present? - errors.add(:tree_sha256, '種類がリダイレクトの場合は空である必要があります.') - end - - if lines_count.to_i > 0 - errors.add(:lines_count, '種類がリダイレクトの場合は 0 である必要があります.') - end - end - end -end diff --git a/backend/app/models/wiki_revision_line.rb b/backend/app/models/wiki_revision_line.rb deleted file mode 100644 index 8d58642..0000000 --- a/backend/app/models/wiki_revision_line.rb +++ /dev/null @@ -1,8 +0,0 @@ -class WikiRevisionLine < ApplicationRecord - belongs_to :wiki_revision - belongs_to :wiki_line - - validates :position, presence: true, - numericality: { only_integer: true, greater_than_or_equal_to: 0 } - validates :position, uniqueness: { scope: :wiki_revision_id } -end diff --git a/backend/app/models/wiki_version.rb b/backend/app/models/wiki_version.rb deleted file mode 100644 index 4f55804..0000000 --- a/backend/app/models/wiki_version.rb +++ /dev/null @@ -1,8 +0,0 @@ -class WikiVersion < ApplicationRecord - include VersionRecord - - belongs_to :wiki_page - - validates :title, presence: true - validates :body, presence: true -end diff --git a/backend/app/representations/wiki_page_repr.rb b/backend/app/representations/wiki_page_repr.rb index e1b780e..2c747ba 100644 --- a/backend/app/representations/wiki_page_repr.rb +++ b/backend/app/representations/wiki_page_repr.rb @@ -2,14 +2,8 @@ module WikiPageRepr - BASE = { methods: [:title, :deprecated_at] }.freeze - module_function - def base wiki_page - wiki_page.as_json(BASE) - end - def tag_description description tag = description.tag { 'id' => description.id, @@ -29,8 +23,4 @@ module WikiPageRepr def tag_group tag_group TagGroupRepr.tag_group(tag_group).merge('resource_type' => 'tag_group') end - - def many wiki_pages - wiki_pages.map { |p| base(p) } - end end diff --git a/backend/app/services/tag_description_operation.rb b/backend/app/services/tag_description_operation.rb index c0d0d11..69020dd 100644 --- a/backend/app/services/tag_description_operation.rb +++ b/backend/app/services/tag_description_operation.rb @@ -1,18 +1,20 @@ class TagDescriptionOperation LANGUAGE_CODE = 'ja' + class Conflict < StandardError + ; + end + def self.create!(tag:, body:, created_by_user:, message: nil) ApplicationRecord.transaction do tag.lock! raise ActiveRecord::RecordNotUnique if TagDescription.exists?( tag_id: tag.id, language_code: LANGUAGE_CODE) - page = Wiki::CompatibilityWriter.create!( - tag_name: compatibility_tag_name(tag), body:, created_by_user:, message:) - revision = page.wiki_revisions.order(id: :desc).first - record_version!(tag:, revision:, body: revision.body, user: created_by_user, - description: nil) - page + description = TagDescription.new(tag:, language_code: LANGUAGE_CODE) + body = normalise_body!(description, body) + record_version!(description:, body:, user: created_by_user) + description end end @@ -22,51 +24,47 @@ class TagDescriptionOperation tag.lock! description = TagDescription.lock.find_by!( tag_id: tag.id, language_code: LANGUAGE_CODE) - tag_name = compatibility_tag_name(tag) - page = WikiPage.lock.find_by(tag_name_id: tag_name.id) - unless page - versions = TagDescriptionVersion.where(tag_id: tag.id, language_code: LANGUAGE_CODE) - if base_revision_id.present? && versions.maximum(:id) != base_revision_id.to_i - raise Wiki::Commit::Conflict, '競合が発生してゐます.' - end - page = Wiki::CompatibilityWriter.create!( - tag_name:, body:, created_by_user: updated_by_user, message:) - revision = page.current_revision - record_version!(tag:, revision:, body: revision.body, - user: updated_by_user, description:) - next revision + body = normalise_body!(description, body) + latest = TagDescriptionVersion.find_by( + tag_id: tag.id, language_code: LANGUAGE_CODE, + version_no: description.version_no) + if base_revision_id.present? && latest&.id != base_revision_id.to_i + raise Conflict, '競合が発生してゐます.' end - revision = Wiki::CompatibilityWriter.update!( - page:, body:, updated_by_user:, message:, - base_revision_id:) - next unless revision + next if latest && normalise_body(description.body) == body - record_version!(tag:, revision:, body: revision.body, - user: updated_by_user, description:) - revision + record_version!(description:, body:, user: updated_by_user) end end - def self.compatibility_tag_name tag - tag.tag_name(LANGUAGE_CODE) || TagName.find_by!(id: tag.tag_name_id, - language_code: LANGUAGE_CODE) + def self.record_version! description:, body:, user: + creating = description.new_record? + version_no = creating ? 1 : description.version_no + 1 + version = TagDescriptionVersion.create!( + tag_id: description.tag_id, language_code: description.language_code, + version_no:, event_type: creating ? 'create' : 'update', body:, + source_language_code: description.source_language_code, + source_version_no: description.source_version_no, + created_by_user_id: user.id, created_at: Time.current) + description.created_by_user_id = user.id if creating + description.update!(body:, version_no:, updated_by_user_id: user.id) + version end - - def self.record_version! tag:, revision:, body:, user:, description: - version_no = description ? description.version_no + 1 : 1 - TagDescriptionVersion.create!( - id: revision.id, tag_id: tag.id, language_code: LANGUAGE_CODE, - version_no:, event_type: description ? 'update' : 'create', body:, - created_by_user_id: user.id, created_at: revision.created_at) - if description - description.update!(body:, version_no:, updated_by_user_id: user.id) - else - TagDescription.create!( - tag_id: tag.id, - language_code: LANGUAGE_CODE, version_no:, body:, - created_by_user_id: user.id, updated_by_user_id: user.id) - end - end - private_class_method :record_version! + + def self.normalise_body body + body.to_s.gsub(/\r\n?/, "\n") + .encode('UTF-8', invalid: :replace, undef: :replace, replace: '🖕') + .sub(/\n+\z/, '') + end + private_class_method :normalise_body + + def self.normalise_body! description, body + normalised = normalise_body(body) + return normalised if normalised.present? + + description.errors.add(:body, :blank) + raise ActiveRecord::RecordInvalid, description + end + private_class_method :normalise_body! end diff --git a/backend/app/services/tag_group_operation.rb b/backend/app/services/tag_group_operation.rb index 985276e..b565486 100644 --- a/backend/app/services/tag_group_operation.rb +++ b/backend/app/services/tag_group_operation.rb @@ -1,10 +1,8 @@ class TagGroupOperation MAX_PLACEMENT_DEPTH = 5 - PREFIX = TagGroupRepr::PREFIX - def self.create!(name:, description:, members:, placements:, actor:, - require_compatibility: false, compatibility_body: nil) - group, sync_issue = ApplicationRecord.transaction do + def self.create!(name:, description:, members:, placements:, actor:) + ApplicationRecord.transaction do group = TagGroup.new(version_no: 1, created_by_user: actor, updated_by_user: actor) attributes = prepare!(group:, name:, description:, members:, placements:) @@ -13,21 +11,15 @@ class TagGroupOperation group.save! persist_structure!(group:, attributes:, actor:) persist_version!(group:, attributes:, actor:, event_type: 'create') - issue = sync_compatibility!(group:, actor:, old_name: nil, - require_compatibility:, compatibility_body:) - [group, issue] + group end - report_sync_issue(group, actor, sync_issue) - group end - def self.update!(group:, name:, description:, members:, placements:, actor:, - require_compatibility: false, compatibility_body: nil) - group, sync_issue = ApplicationRecord.transaction do + def self.update!(group:, name:, description:, members:, placements:, actor:) + ApplicationRecord.transaction do group.lock! - old_name = group.name attributes = prepare!(group:, name:, description:, members:, placements:) - next [group, nil] if unchanged?(group, attributes) + next group if unchanged?(group, attributes) group.update!(name: attributes.fetch(:name), description: attributes.fetch(:description), @@ -35,12 +27,8 @@ class TagGroupOperation updated_by_user: actor) persist_structure!(group:, attributes:, actor:) persist_version!(group:, attributes:, actor:, event_type: 'update') - issue = sync_compatibility!(group:, actor:, old_name:, - require_compatibility:, compatibility_body:) - [group, issue] + group end - report_sync_issue(group, actor, sync_issue) - group end def self.prepare!(group:, name:, description:, members:, placements:) @@ -313,57 +301,6 @@ class TagGroupOperation end private_class_method :persist_version! - def self.sync_compatibility!(group:, actor:, old_name:, - require_compatibility:, compatibility_body:) - representation = TagGroupRepr.one(group) - body = begin - TagGroupMarkdownRenderer.render(representation) - rescue TagGroupMarkdownRenderer::UnsupportedStructure => error - if compatibility_body.present? - compatibility_body - elsif require_compatibility - invalid!(group, :base, error.message) - else - return error.message - end - end - invalid!(group, :base, '本文は必須です.') if body.blank? - - title = "#{ PREFIX }#{ group.name }" - old_title = old_name && "#{ PREFIX }#{ old_name }" - page = if old_title - WikiPage.joins(:tag_name).find_by(tag_names: { name: old_title }) - else - nil - end - page ||= WikiPage.joins(:tag_name).find_by(tag_names: { name: title }) - - if page - tag_name = page.tag_name - tag_name.update!(name: title) if tag_name.name != title - Wiki::Commit.legacy_content!(page:, body:, created_user: actor, message: nil, - base_revision_id: nil) - else - tag_name = TagName.find_or_initialize_by(language_code: 'ja', name: title) - tag_name.script_code = 'Jpan' - tag_name.primary_flg = true - tag_name.auto_generated = false - tag_name.save! - Wiki::CompatibilityWriter.create!(tag_name:, body:, created_by_user: actor) - end - nil - end - private_class_method :sync_compatibility! - - def self.report_sync_issue(group, actor, reason) - return unless reason - - payload = { status: 'unsupported_structure', tag_group_id: group.id, - version_no: group.version_no, user_id: actor&.id, reason: } - Rails.logger.warn("tag_group_wiki_sync_skipped #{ payload.to_json }") - end - private_class_method :report_sync_issue - def self.placement_references(nodes) nodes.flat_map do |node| own = if node.key?('tag_id') diff --git a/backend/app/services/wiki/commit.rb b/backend/app/services/wiki/commit.rb deleted file mode 100644 index d18bd63..0000000 --- a/backend/app/services/wiki/commit.rb +++ /dev/null @@ -1,164 +0,0 @@ -require 'digest' - - -module Wiki - class Commit - class Conflict < StandardError - ; - end - - def self.create_content! tag_name:, body:, created_by_user:, message: nil - if tag_name.tag && tag_name.language_code == 'ja' && !tag_name.name.include?(':') - return TagDescriptionOperation.create!( - tag: tag_name.tag, body:, created_by_user:, message:) - end - - tag_name.errors.add(:base, :invalid) - raise ActiveRecord::RecordInvalid, tag_name - end - - def self.create_legacy_content! tag_name:, body:, created_by_user:, message: nil - normalised = normalise_body(body) - - page = WikiPage.new(tag_name:, - body: normalised, - created_user: created_by_user, - updated_user: created_by_user) - - if normalised.blank? - page.errors.add(:body, :blank) - raise ActiveRecord::RecordInvalid, page - end - - ActiveRecord::Base.transaction do - page.save! - - new(page:, created_user: created_by_user).content!( - body: normalised, - message:, - base_revision_id: nil) - - page - end - end - - def self.content! page:, body:, created_user:, message: nil, base_revision_id: nil - description = TagDescription.find_by(tag_id: page.tag_name.tag_id, language_code: 'ja') - description ||= TagDescription.joins(:tag).find_by( - tags: { tag_name_id: page.tag_name_id }, language_code: 'ja') - if description && !page.title.include?(':') - return TagDescriptionOperation.update!( - tag: description.tag, body:, updated_by_user: created_user, - message:, base_revision_id:) - end - - page.errors.add(:base, :invalid) - raise ActiveRecord::RecordInvalid, page - end - - def self.legacy_content! page:, body:, created_user:, message: nil, - base_revision_id: nil - new(page:, created_user:).content!(body:, message:, base_revision_id:) - end - - def self.redirect! page:, redirect_page:, created_user:, message: nil, base_revision_id: nil - new(page:, created_user:).redirect!(redirect_page:, message:, base_revision_id:) - end - - def initialize page:, created_user: - @page = page - @created_user = created_user - end - - def content! body:, message:, base_revision_id: - normalised = self.class.normalise_body(body) - if normalised.blank? - @page.errors.add(:body, :blank) - raise ActiveRecord::RecordInvalid, @page - end - - lines = split_lines(normalised) - - line_shas = lines.map { |line| Digest::SHA256.hexdigest(line) } - tree_sha = Digest::SHA256.hexdigest(line_shas.join(',')) - - ActiveRecord::Base.transaction do - @page.lock! - - if base_revision_id.present? - current_id = @page.wiki_revisions.maximum(:id) - if current_id && current_id != base_revision_id.to_i - raise Conflict, - "競合が発生してゐます" + - "(現在の Id.:#{ current_id },ベース Id.:#{ base_revision_id })." - end - end - - line_id_by_sha = upsert_lines!(lines, line_shas) - line_ids = line_shas.map { |sha| line_id_by_sha.fetch(sha) } - @page.update!(body: normalised) - - WikiVersionRecorder.record!( - page: @page, - event_type: @page.wiki_versions.exists? ? :update : :create, - reason: message, - created_by_user: @created_user) - - rev = WikiRevision.create!( - wiki_page: @page, - base_revision_id:, - created_user: @created_user, - kind: :content, - redirect_page_id: nil, - message:, - lines_count: lines.length, - tree_sha256: tree_sha) - - rows = line_ids.each_with_index.map do |line_id, pos| - { wiki_revision_id: rev.id, wiki_line_id: line_id, position: pos } - end - WikiRevisionLine.insert_all!(rows) if rows.any? - rev - end - end - - def redirect!(redirect_page:, message:, base_revision_id:) = raise '廃止しました.' - - def self.normalise_body body - s = body.to_s - s.gsub!(/\r\n?/, "\n") - s.encode('UTF-8', invalid: :replace, undef: :replace, replace: '🖕') - s.gsub(/\n+$/, '') - end - - private - - def split_lines(body) = body.split("\n") - - def upsert_lines! lines, line_shas - now = Time.current - - id_by_sha = WikiLine.where(sha256: line_shas).pluck(:sha256, :id).to_h - - missing_by_sha = { } - - line_shas.each_with_index do |sha, i| - next if id_by_sha.key?(sha) - next if missing_by_sha.key?(sha) - - missing_by_sha[sha] = { - sha256: sha, - body: lines[i], - created_at: now, - updated_at: now } - end - - if missing_by_sha.any? - WikiLine.upsert_all(missing_by_sha.values) - id_by_sha = WikiLine.where(sha256: line_shas).pluck(:sha256, :id).to_h - end - - id_by_sha - end - end -end diff --git a/backend/app/services/wiki/compatibility_writer.rb b/backend/app/services/wiki/compatibility_writer.rb deleted file mode 100644 index c7bcaa1..0000000 --- a/backend/app/services/wiki/compatibility_writer.rb +++ /dev/null @@ -1,43 +0,0 @@ -module Wiki - class CompatibilityWriter - def self.create! tag_name:, body:, created_by_user:, message: nil - ApplicationRecord.transaction do - tag_name.lock! - page = WikiPage.lock.find_by(tag_name_id: tag_name.id) - if page - update!(page:, body:, updated_by_user: created_by_user, message:) - page - else - Commit.create_legacy_content!(tag_name:, body:, created_by_user:, message:) - end - end - end - - def self.update! page:, body:, updated_by_user:, message: nil, - base_revision_id: nil - normalised = Commit.normalise_body(body) - if normalised.blank? - return Commit.legacy_content!( - page:, body: normalised, created_user: updated_by_user, message:, - base_revision_id:) - end - - if base_revision_id.present? - current_id = page.wiki_revisions.maximum(:id) - if current_id && current_id != base_revision_id.to_i - raise Commit::Conflict, - "競合が発生してゐます" + - "(現在の Id.:#{ current_id },ベース Id.:#{ base_revision_id })." - end - end - - if Commit.normalise_body(page.body) == normalised && page.wiki_revisions.exists? - return - end - - Commit.legacy_content!( - page:, body: normalised, created_user: updated_by_user, message:, - base_revision_id:) - end - end -end diff --git a/backend/app/services/wiki_version_recorder.rb b/backend/app/services/wiki_version_recorder.rb deleted file mode 100644 index 41e1db9..0000000 --- a/backend/app/services/wiki_version_recorder.rb +++ /dev/null @@ -1,21 +0,0 @@ -class WikiVersionRecorder < VersionRecorder - def self.record! page:, event_type:, reason: nil, created_by_user: - new(page:, event_type:, reason:, created_by_user:).record! - end - - def initialize page:, event_type:, reason: nil, created_by_user: - @reason = reason - super(record: page, event_type:, created_by_user:) - end - - private - - def version_class = WikiVersion - def version_association = :wiki_versions - def record_key = :wiki_page - - def snapshot_attributes = { - title: @record.title, - body: @record.body, - reason: @reason } -end