このコミットが含まれているのは:
2026-10-04 23:06:14 +09:00
コミット d63e0b95fa
13個のファイルの変更、165行の追加、115行の削除
+1 -1
ファイルの表示
@@ -46,7 +46,7 @@ class TagDescriptionsController < ApplicationController
return render_unprocessable_entity('本文は必須です.', field: :body) if body.blank?
description = TagDescription.find_by(tag_id: params[:id], language_code: 'ja')
return head :not_found unless description
return create unless description
TagDescriptionOperation.update!(
tag: description.tag, body:, updated_by_user: current_user)
+3 -20
ファイルの表示
@@ -464,7 +464,6 @@ class TagsController < ApplicationController
old_name = tag.name(locale.language_code)
name_changed = name != old_name
wiki_page = tag.tag_name(locale.language_code)&.wiki_page if name_changed
if tag.deprecated? == deprecated
tag.update!(category:)
@@ -485,9 +484,7 @@ class TagsController < ApplicationController
tag,
event_type: :update,
created_by_user: current_user,
language_code: locale.language_code,
name_changed:,
wiki_page:)
language_code: locale.language_code)
end
render json: TagRepr.base(tag.reload, language_code: locale.language_code)
@@ -518,7 +515,6 @@ class TagsController < ApplicationController
old_name = tag.name(locale.language_code)
name_changed = name.present? && name != old_name
wiki_page = tag.tag_name(locale.language_code)&.wiki_page if name_changed
rename_tag_name!(locale, tag, name) if name_changed
tag.update!(category:) if category.present?
@@ -532,9 +528,7 @@ class TagsController < ApplicationController
tag,
event_type: :update,
created_by_user: current_user,
language_code: locale.language_code,
name_changed:,
wiki_page:)
language_code: locale.language_code)
end
render json: TagRepr.base(tag.reload, language_code: locale.language_code)
@@ -722,19 +716,8 @@ class TagsController < ApplicationController
language_code:))
end
def record_tag_version! tag, event_type:, created_by_user:, language_code: 'ja',
name_changed: false, wiki_page: nil
def record_tag_version! tag, event_type:, created_by_user:, language_code: 'ja'
TagVersionRecorder.record!(tag:, event_type:, created_by_user:, language_code:)
return unless name_changed
wiki_page ||= tag.tag_name('ja')&.wiki_page
return unless wiki_page&.wiki_versions&.exists?
WikiVersionRecorder.record!(
page: wiki_page,
event_type: :update,
created_by_user:)
end
def validate_tag_rename locale, tag, name
+16 -8
ファイルの表示
@@ -118,15 +118,23 @@ class WikiPagesController < ApplicationController
return render_unprocessable_entity('タイトルは必須です.', field: :title) if title.blank?
return render_unprocessable_entity('本文は必須です.', field: :body) if body.blank?
page = ApplicationRecord.transaction do
tag_name = TagName.find_or_create_by!(language_code: 'ja', name: title) do |name|
name.script_code = 'Jpan'
name.primary_flg = true
name.auto_generated = false
end
page = if title.include?(':')
ApplicationRecord.transaction do
tag_name = TagName.find_or_create_by!(language_code: 'ja', name: title) do |name|
name.script_code = 'Jpan'
name.primary_flg = true
name.auto_generated = false
end
Wiki::Commit.create_content!(
tag_name:, body:, created_by_user: current_user, message:)
Wiki::Commit.create_legacy_content!(
tag_name:, body:, created_by_user: current_user, message:)
end
else
tag_name = TagName.find_by(language_code: 'ja', name: title)
return render_unprocessable_entity field: :title unless tag_name&.tag
TagDescriptionOperation.create!(
tag: tag_name.tag, body:, created_by_user: current_user, message:)
end
render json: WikiPageRepr.base(page), status: :created
+2 -4
ファイルの表示
@@ -20,11 +20,9 @@ class TagDescriptionOperation
base_revision_id: nil)
ApplicationRecord.transaction do
tag.lock!
description = TagDescription.lock.find_by(
description = TagDescription.lock.find_by!(
tag_id: tag.id, language_code: LANGUAGE_CODE)
description&.lock!
tag_name = tag.tag_name(LANGUAGE_CODE)
page = WikiPage.lock.find_by!(tag_name:)
page = WikiPage.lock.find(description.id)
revision = Wiki::CompatibilityWriter.update!(
page:, body:, updated_by_user:, message:,
base_revision_id:)
+3 -3
ファイルの表示
@@ -42,10 +42,10 @@ module Wiki
end
def self.content! page:, body:, created_user:, message: nil, base_revision_id: nil
tag = page.tag_name.tag
if tag && page.tag_name.language_code == 'ja'
description = TagDescription.find_by(id: page.id)
if description
return TagDescriptionOperation.update!(
tag:, body:, updated_by_user: created_user,
tag: description.tag, body:, updated_by_user: created_user,
message:, base_revision_id:)
end
+4 -8
ファイルの表示
@@ -1,12 +1,4 @@
Rails.application.routes.draw do
get 'tags/name/:name/description', to: 'tag_descriptions#show_by_title'
scope 'tags/:id/description', controller: :tag_descriptions do
get '', action: :show
post '', action: :create
put '', action: :update
end
resources :nico_tags, path: 'tags/nico', only: [:index, :show, :update]
scope 'tags/:parent_id/children', controller: :tag_children do
@@ -22,6 +14,7 @@ Rails.application.routes.draw do
scope :name do
get ':name/deerjikists', action: :deerjikists_by_name
get ':name/description', to: 'tag_descriptions#show_by_title'
get ':name/materials', action: :materials_by_name
get ':name', action: :show_by_name
end
@@ -33,6 +26,9 @@ Rails.application.routes.draw do
get :deerjikists
put :deerjikists, action: :update_deerjikists
get :description, action: 'tag_descriptions#show'
put :description, action: 'tag_descriptions#update'
end
end
+68 -2
ファイルの表示
@@ -52,6 +52,58 @@ RSpec.describe 'Tag description Wiki dual writes', type: :request do
expect(page.wiki_versions.last.body).to eq(version.body)
end
['missing_description', 'orphan_description'].each do |title|
it "rejects the bare title #{ title } without writing any resource" do
create(:tag_name, name: title) if title == 'orphan_description'
before_state = stored_state
post '/wiki', params: { title:, body: 'initial' }, headers: headers
expect(response).to have_http_status(:unprocessable_entity)
expect(json).to include('type' => 'validation_error')
expect(stored_state).to eq(before_state)
end
end
it 'creates a namespaced legacy Wiki resource without a Tag' do
expect do
post '/wiki', params: { title: 'Help:description_write', body: 'legacy' },
headers: headers
end.to change(WikiPage, :count).by(1)
.and change(WikiRevision, :count).by(1)
.and change(WikiVersion, :count).by(1)
.and change(TagDescription, :count).by(0)
.and change(TagDescriptionVersion, :count).by(0)
expect(response).to have_http_status(:created)
page = WikiPage.find(json.fetch('id'))
expect(page.tag_name.tag).to be_nil
expect(page.body).to eq('legacy')
expect(page.current_revision.body).to eq('legacy')
end
it 'creates only legacy Wiki for a namespaced title with an existing Tag' do
namespaced_name = create(:tag_name, name: 'help:foo')
namespaced_tag = create(:tag, primary_tag_name: namespaced_name)
expect do
post '/wiki', params: { title: namespaced_name.name, body: 'legacy' },
headers: headers
end.to change(WikiPage, :count).by(1)
.and change(WikiRevision, :count).by(1)
.and change(WikiVersion, :count).by(1)
.and change(TagName, :count).by(0)
.and change(TagDescription, :count).by(0)
.and change(TagDescriptionVersion, :count).by(0)
expect(response).to have_http_status(:created)
page = WikiPage.find(json.fetch('id'))
expect(page).to have_attributes(tag_name_id: namespaced_name.id, body: 'legacy')
expect(page.tag_name.tag).to eq(namespaced_tag)
expect(page.current_revision.body).to eq('legacy')
expect(page.wiki_versions.last.body).to eq('legacy')
end
[WikiPage, WikiRevision, WikiVersion, TagDescription, TagDescriptionVersion].each do |model|
it "rolls back both systems when #{ model.name } persistence fails" do
tag_name
@@ -101,6 +153,20 @@ RSpec.describe 'Tag description Wiki dual writes', type: :request do
expect(page.wiki_versions.order(:version_no).last.body).to eq(version.body)
end
it 'uses description identity even when its Wiki TagName no longer owns the Tag' do
tag_name.update_columns(tag_id: nil)
expect { update_description }
.to change(WikiRevision, :count).by(1)
.and change(WikiVersion, :count).by(1)
.and change(TagDescriptionVersion, :count).by(1)
expect(response).to have_http_status(:ok)
expect(description.reload).to have_attributes(body: 'after', version_no: 2)
expect(TagDescriptionVersion.find(page.reload.current_revision.id))
.to have_attributes(tag_id: tag.id, body: 'after', version_no: 2)
end
it 'does not add body history for a title-only update' do
revision_count = WikiRevision.count
wiki_version_count = WikiVersion.count
@@ -111,10 +177,10 @@ RSpec.describe 'Tag description Wiki dual writes', type: :request do
page_state = page.attributes
description_state = description.attributes
update_description(body: 'before', title: 'description_renamed')
update_description(body: 'before', title: 'Help:description_renamed')
expect(response).to have_http_status(:ok)
expect(tag_name.reload.name).to eq('description_renamed')
expect(tag_name.reload.name).to eq('Help:description_renamed')
expect(WikiRevision.count).to eq(revision_count)
expect(WikiVersion.count).to eq(wiki_version_count)
expect(TagDescriptionVersion.count).to eq(description_version_count)
+8 -18
ファイルの表示
@@ -25,9 +25,10 @@ RSpec.describe 'Tag and wiki history integrity', type: :request do
end
describe 'PATCH /tags/:id' do
it 'records wiki_version when tag name changes and tag has wiki' do
it 'renames the current Wiki resource without adding body history' do
tag = create_tag!(name: 'patch_tag_wiki_before')
wiki_page = create_wiki_for_tag!(tag:, body: 'wiki body before')
wiki_history = wiki_page.wiki_versions.map(&:attributes)
expect {
patch "/tags/#{ tag.id }", params: {
@@ -35,23 +36,17 @@ RSpec.describe 'Tag and wiki history integrity', type: :request do
}
}
.to change(TagVersion, :count).by(2)
.and change(WikiVersion, :count).by(1)
.and change(WikiVersion, :count).by(0)
expect(response).to have_http_status(:ok)
tag.reload
wiki_page.reload
version = wiki_page.wiki_versions.order(:version_no).last
expect(primary_tag_name_for(tag, 'ja').name).to eq('patch_tag_wiki_after')
expect(wiki_page.title).to eq('patch_tag_wiki_after')
expect(version).to have_attributes(
event_type: 'update',
title: 'patch_tag_wiki_after',
body: 'wiki body before',
created_by_user_id: member_user.id
)
expect(wiki_page.wiki_versions.reload.map(&:attributes)).to eq(wiki_history)
end
it 'does not record wiki_version when only category changes' do
@@ -79,9 +74,10 @@ RSpec.describe 'Tag and wiki history integrity', type: :request do
end
describe 'PUT /tags/:id' do
it 'records wiki_version when tag name changes and tag has wiki' do
it 'renames the current Wiki resource without adding body history' do
tag = create_tag!(name: 'put_tag_wiki_before')
wiki_page = create_wiki_for_tag!(tag:, body: 'wiki body before')
wiki_history = wiki_page.wiki_versions.map(&:attributes)
expect {
put "/tags/#{ tag.id }", params: {
@@ -93,23 +89,17 @@ RSpec.describe 'Tag and wiki history integrity', type: :request do
}
}
.to change(TagVersion, :count).by(2)
.and change(WikiVersion, :count).by(1)
.and change(WikiVersion, :count).by(0)
expect(response).to have_http_status(:ok)
tag.reload
wiki_page.reload
version = wiki_page.wiki_versions.order(:version_no).last
expect(primary_tag_name_for(tag, 'ja').name).to eq('put_tag_wiki_after')
expect(wiki_page.title).to eq('put_tag_wiki_after')
expect(version).to have_attributes(
event_type: 'update',
title: 'put_tag_wiki_after',
body: 'wiki body before',
created_by_user_id: member_user.id
)
expect(wiki_page.wiki_versions.reload.map(&:attributes)).to eq(wiki_history)
end
it 'does not record wiki_version when only category changes' do
+22 -35
ファイルの表示
@@ -727,7 +727,7 @@ RSpec.describe 'Tags API', type: :request do
expect(tag.reload.category).to eq('general')
end
it 'PATCH で tag の name を変更すると対応する wiki version を作成する' do
it 'PATCH の名称変更で Wiki の本文履歴を追加しない' do
wiki_page =
Wiki::Commit.create_content!(
tag_name: primary_tag_name_for(tag, 'ja'),
@@ -735,24 +735,20 @@ RSpec.describe 'Tags API', type: :request do
created_by_user: member_user,
message: 'init')
wiki_history = wiki_page.wiki_versions.map(&:attributes)
expect {
patch "/tags/#{ tag.id }", params: {
name: 'patch_wiki_renamed_tag',
}
}
.to change(TagVersion, :count).by(2)
.and change(WikiVersion, :count).by(1)
.and change(WikiVersion, :count).by(0)
expect(response).to have_http_status(:ok)
version = wiki_page.reload.wiki_versions.order(:version_no).last
expect(version).to have_attributes(
event_type: 'update',
title: 'patch_wiki_renamed_tag',
body: 'wiki body before',
created_by_user_id: member_user.id
)
expect(wiki_page.reload.title).to eq('patch_wiki_renamed_tag')
expect(wiki_page.wiki_versions.reload.map(&:attributes)).to eq(wiki_history)
end
it 'tag の category だけを変更しても wiki version は作成しない' do
@@ -1380,7 +1376,7 @@ RSpec.describe 'Tags API', type: :request do
expect(system_tag.category).to eq(old_category)
end
it 'wiki を持つ tag を更新すると wiki version も作成する' do
it 'Wiki を持つ Tag の名称変更で本文履歴を追加しない' do
wiki_page =
Wiki::Commit.create_content!(
tag_name: primary_tag_name_for(tag, 'ja'),
@@ -1395,6 +1391,8 @@ RSpec.describe 'Tags API', type: :request do
message: 'init'
)
wiki_history = wiki_page.wiki_versions.map(&:attributes)
expect {
put "/tags/#{ tag.id }", params: {
name: 'put_wiki_version_tag',
@@ -1405,18 +1403,12 @@ RSpec.describe 'Tags API', type: :request do
}
}
.to change(TagVersion, :count).by(2)
.and change(WikiVersion, :count).by(1)
.and change(WikiVersion, :count).by(0)
expect(response).to have_http_status(:ok)
version = wiki_page.reload.wiki_versions.order(:version_no).last
expect(version).to have_attributes(
event_type: 'update',
title: 'put_wiki_version_tag',
body: 'wiki body before',
created_by_user_id: member_user.id
)
expect(wiki_page.reload.title).to eq('put_wiki_version_tag')
expect(wiki_page.wiki_versions.reload.map(&:attributes)).to eq(wiki_history)
end
it 'wiki を持つ tag を旧 alias へ戻しても wiki を新しい正本名へ移す' do
@@ -1426,6 +1418,7 @@ RSpec.describe 'Tags API', type: :request do
body: 'wiki body before',
created_by_user: member_user,
message: 'init')
wiki_history = wiki_page.wiki_versions.map(&:attributes)
expect {
put "/tags/#{ tag.id }", params: {
@@ -1445,7 +1438,7 @@ RSpec.describe 'Tags API', type: :request do
}
}
.to change(TagVersion, :count).by(3)
.and change(WikiVersion, :count).by(2)
.and change(WikiVersion, :count).by(0)
expect(response).to have_http_status(:ok)
@@ -1457,10 +1450,8 @@ RSpec.describe 'Tags API', type: :request do
expect(TagName.find_by!(language_code: 'ja', name: 'put_wiki_roundtrip_b'))
.to have_attributes(tag_id: tag.id, primary_flg: false)
versions = wiki_page.wiki_versions.order(:version_no).last(2)
expect(versions.map(&:event_type)).to eq(['update', 'update'])
expect(versions.map(&:title)).to eq(['put_wiki_roundtrip_b', 'spec_tag'])
expect(wiki_page.title).to eq('spec_tag')
expect(wiki_page.wiki_versions.reload.map(&:attributes)).to eq(wiki_history)
end
it '別名を他 tag から奪った場合、奪はれた側の tag version も作成する' do
@@ -1570,7 +1561,7 @@ RSpec.describe 'Tags API', type: :request do
expect(TagImplication.where(tag:, parent_tag: child)).not_to exist
end
it 'tag の name を変更すると対応する wiki version を作成する' do
it 'Tag の名称変更で Wiki の本文履歴を追加しない' do
wiki_page =
Wiki::Commit.create_content!(
tag_name: primary_tag_name_for(tag, 'ja'),
@@ -1578,6 +1569,8 @@ RSpec.describe 'Tags API', type: :request do
created_by_user: member_user,
message: 'init')
wiki_history = wiki_page.wiki_versions.map(&:attributes)
expect {
put "/tags/#{ tag.id }", params: {
name: 'put_wiki_renamed_tag',
@@ -1588,18 +1581,12 @@ RSpec.describe 'Tags API', type: :request do
}
}
.to change(TagVersion, :count).by(2)
.and change(WikiVersion, :count).by(1)
.and change(WikiVersion, :count).by(0)
expect(response).to have_http_status(:ok)
version = wiki_page.reload.wiki_versions.order(:version_no).last
expect(version).to have_attributes(
event_type: 'update',
title: 'put_wiki_renamed_tag',
body: 'wiki body before',
created_by_user_id: member_user.id
)
expect(wiki_page.reload.title).to eq('put_wiki_renamed_tag')
expect(wiki_page.wiki_versions.reload.map(&:attributes)).to eq(wiki_history)
end
end
end
+10 -8
ファイルの表示
@@ -21,7 +21,7 @@ RSpec.describe 'Wiki history integrity', type: :request do
expect {
post '/wiki',
params: {
title: 'wiki_history_create_atomic',
title: 'Help:wiki_history_create_atomic',
body: "a\nb\nc",
message: 'initial commit',
},
@@ -37,7 +37,7 @@ RSpec.describe 'Wiki history integrity', type: :request do
revision = page.current_revision
version = page.wiki_versions.order(:version_no).last
expect(page.title).to eq('wiki_history_create_atomic')
expect(page.title).to eq('Help:wiki_history_create_atomic')
expect(page.body).to eq("a\nb\nc")
expect(revision).to be_content
@@ -47,7 +47,7 @@ RSpec.describe 'Wiki history integrity', type: :request do
expect(version).to have_attributes(
version_no: 1,
event_type: 'create',
title: 'wiki_history_create_atomic',
title: 'Help:wiki_history_create_atomic',
body: "a\nb\nc",
reason: 'initial commit',
created_by_user_id: user.id
@@ -58,7 +58,7 @@ RSpec.describe 'Wiki history integrity', type: :request do
expect {
post '/wiki',
params: {
title: 'wiki_history_blank_body',
title: 'Help:wiki_history_blank_body',
body: "\r\n\r\n",
message: 'blank',
},
@@ -67,16 +67,17 @@ RSpec.describe 'Wiki history integrity', type: :request do
.not_to change(WikiPage, :count)
expect(response).to have_http_status(:unprocessable_entity)
expect(WikiPage.joins(:tag_name).where(tag_names: { name: 'wiki_history_blank_body' })).not_to exist
expect(WikiPage.joins(:tag_name)
.where(tag_names: { name: 'Help:wiki_history_blank_body' })).not_to exist
end
it 'returns 422 and creates no partial page when title already exists' do
create_wiki_page(title: 'wiki_history_duplicate_title', body: 'first')
create_wiki_page(title: 'Help:wiki_history_duplicate_title', body: 'first')
expect {
post '/wiki',
params: {
title: 'wiki_history_duplicate_title',
title: 'Help:wiki_history_duplicate_title',
body: 'second',
message: 'duplicate',
},
@@ -85,7 +86,8 @@ RSpec.describe 'Wiki history integrity', type: :request do
.not_to change(WikiPage, :count)
expect(response).to have_http_status(:unprocessable_entity)
expect(WikiPage.joins(:tag_name).where(tag_names: { name: 'wiki_history_duplicate_title' }).count).to eq(1)
expect(WikiPage.joins(:tag_name)
.where(tag_names: { name: 'Help:wiki_history_duplicate_title' }).count).to eq(1)
end
end
+7 -6
ファイルの表示
@@ -111,7 +111,7 @@ RSpec.describe 'Wiki API', type: :request do
context 'when success' do
it 'creates wiki_page and first content revision' do
expect do
post endpoint, params: { title: 'TestPage', body: "a\nb\nc", message: 'init' },
post endpoint, params: { title: 'Help:TestPage', body: "a\nb\nc", message: 'init' },
headers: auth_headers(member)
end
.to change(WikiPage, :count).by(1)
@@ -121,7 +121,7 @@ RSpec.describe 'Wiki API', type: :request do
expect(response).to have_http_status(:created)
page_id = json.fetch('id')
expect(json.fetch('title')).to eq('TestPage')
expect(json.fetch('title')).to eq('Help:TestPage')
created_page = WikiPage.find(page_id)
version = created_page.wiki_versions.order(:version_no).last
@@ -129,7 +129,7 @@ RSpec.describe 'Wiki API', type: :request do
expect(version).to have_attributes(
version_no: 1,
event_type: 'create',
title: 'TestPage',
title: 'Help:TestPage',
body: "a\nb\nc",
created_by_user_id: member.id
)
@@ -151,7 +151,7 @@ RSpec.describe 'Wiki API', type: :request do
WikiLine.create!(sha256: Digest::SHA256.hexdigest('a'), body: 'a', created_at: Time.current, updated_at: Time.current)
post endpoint,
params: { title: 'Reuse', body: "a\na" },
params: { title: 'Help:Reuse', body: "a\na" },
headers: auth_headers(member)
page = WikiPage.find(JSON.parse(response.body).fetch('id'))
@@ -166,7 +166,7 @@ RSpec.describe 'Wiki API', type: :request do
duplicated = 'duplicated_line_for_wiki_line_upsert_spec'
post endpoint,
params: { title: 'DuplicateNewLine', body: "#{ duplicated }\n#{ duplicated }" },
params: { title: 'Help:DuplicateNewLine', body: "#{ duplicated }\n#{ duplicated }" },
headers: auth_headers(member)
expect(response).to have_http_status(:created)
@@ -182,7 +182,8 @@ RSpec.describe 'Wiki API', type: :request do
it 'normalises CRLF and strips trailing newlines' do
post endpoint,
params: { title: 'NormalisedBody', body: "a\r\nb\r\n\r\n", message: 'normalise' },
params: { title: 'Help:NormalisedBody',
body: "a\r\nb\r\n\r\n", message: 'normalise' },
headers: auth_headers(member)
expect(response).to have_http_status(:created)
+2 -2
ファイルの表示
@@ -44,12 +44,12 @@ RSpec.describe 'Wiki title collision', type: :request do
end
it 'returns 422 when creating wiki with existing title' do
create_wiki_page(title: 'wiki_collision_create', body: 'already exists')
create_wiki_page(title: 'Help:wiki_collision_create', body: 'already exists')
expect {
post '/wiki',
params: {
title: 'wiki_collision_create',
title: 'Help:wiki_collision_create',
body: 'new body',
message: 'duplicate create',
},
+19
ファイルの表示
@@ -14,6 +14,25 @@ RSpec.describe 'Unmigrated Wiki compatibility', type: :request do
before { expect(TagDescription.exists?(id: page.id)).to be(false) }
it 'keeps a Tag-linked legacy page in the legacy domain when its title changes' do
create(:tag, primary_tag_name: tag_name)
expect do
put "/wiki/#{ page.id }",
params: { title: 'legacy_tag_renamed', body: 'changed legacy body',
message: 'legacy rename', base_revision_id: last_revision.id },
headers: { 'X-Transfer-Code' => user.inheritance_code }
end.to change(WikiRevision, :count).by(1)
.and change(WikiVersion, :count).by(1)
.and change(TagDescription, :count).by(0)
.and change(TagDescriptionVersion, :count).by(0)
expect(response).to have_http_status(:ok)
expect(page.reload).to have_attributes(body: 'changed legacy body')
expect(page.title).to eq('legacy_tag_renamed')
expect(page.current_revision).to have_attributes(message: 'legacy rename')
end
it 'shows legacy current and historical content by ID' do
get "/wiki/#{ page.id }"
expect(response).to have_http_status(:ok)