Browse Source

#321

feature/321
みてるぞ 1 week ago
parent
commit
478bc15ad0
3 changed files with 238 additions and 14 deletions
  1. +18
    -13
      backend/app/controllers/tag_versions_controller.rb
  2. +218
    -0
      backend/spec/requests/tag_versions_spec.rb
  3. +2
    -1
      frontend/src/components/TopNav.tsx

+ 18
- 13
backend/app/controllers/tag_versions_controller.rb View File

@@ -39,18 +39,23 @@ class TagVersionsController < ApplicationController
cur_aliases = split_values(row.aliases)
prev_aliases = split_values(row.attributes['prev_aliases'])

cur_parent_tags =
TagRepr.many(
Tag
.includes(:tag_name, :materials, { tag_name: :wiki_page })
.where(id: split_parent_tag_ids(row.parent_tag_ids))
.to_a)
prev_parent_tags =
TagRepr.many(
Tag
.includes(:tag_name, :materials, { tag_name: :wiki_page })
.where(id: split_parent_tag_ids(row.attributes['prev_parent_tag_ids']))
.to_a)
cur_parent_tag_ids = split_parent_tag_ids(row.parent_tag_ids)
prev_parent_tag_ids = split_parent_tag_ids(row.attributes['prev_parent_tag_ids'])

all_parent_tag_ids = (cur_parent_tag_ids | prev_parent_tag_ids)

tags_by_id =
Tag
.includes(:tag_name, :materials, { tag_name: :wiki_page })
.where(id: all_parent_tag_ids)
.index_by(&:id)

parent_tags =
build_version_values(cur_parent_tag_ids, prev_parent_tag_ids, key: :tag_id)
.map do |h|
{ tag: TagRepr.base(tags_by_id[h[:tag_id]]),
type: h[:type] }
end

{ tag_id: row.tag_id,
version_no: row.version_no,
@@ -58,7 +63,7 @@ class TagVersionsController < ApplicationController
name: { current: row.name, prev: row.attributes['prev_name'] },
category: { current: row.category, prev: row.attributes['prev_category'] },
aliases: build_version_values(cur_aliases, prev_aliases, key: :name),
parent_tags: build_version_values(cur_parent_tags, prev_parent_tags, key: :tag),
parent_tags:,
created_at: row.created_at.iso8601,
created_by_user: row.created_by_user_id &&
{ id: row.created_by_user_id,


+ 218
- 0
backend/spec/requests/tag_versions_spec.rb View File

@@ -0,0 +1,218 @@
require 'rails_helper'

RSpec.describe 'TagVersions API', type: :request do
let(:member) { create(:user, :member, name: 'version member') }

let!(:tag) { create(:tag, name: 'tag_versions_target', category: :general) }
let!(:other_tag) { create(:tag, name: 'tag_versions_other', category: :general) }

let!(:parent_shared) { create(:tag, name: 'parent_shared', category: :general) }
let!(:parent_old) { create(:tag, name: 'parent_old', category: :general) }
let!(:parent_new) { create(:tag, name: 'parent_new', category: :general) }
let!(:other_parent) { create(:tag, name: 'other_parent', category: :general) }

let(:t_v1) { Time.zone.local(2020, 1, 1, 12, 0, 0) }
let(:t_v2) { Time.zone.local(2020, 1, 2, 12, 0, 0) }
let(:t_other) { Time.zone.local(2020, 1, 3, 12, 0, 0) }

def create_tag_version!(
tag:,
version_no:,
event_type:,
name:,
category:,
aliases: [],
parent_tags: [],
created_by_user:,
created_at:
)
TagVersion.create!(
tag: tag,
version_no: version_no,
event_type: event_type,
name: name,
category: category,
aliases: Array(aliases).join(' '),
parent_tag_ids: Array(parent_tags).map(&:id).join(' '),
created_by_user: created_by_user,
created_at: created_at
)
end

let!(:v1) do
create_tag_version!(
tag: tag,
version_no: 1,
event_type: 'create',
name: 'old_tag_name',
category: 'general',
aliases: ['alias_shared', 'alias_old'],
parent_tags: [parent_shared, parent_old],
created_by_user: member,
created_at: t_v1
)
end

let!(:v2) do
create_tag_version!(
tag: tag,
version_no: 2,
event_type: 'update',
name: 'new_tag_name',
category: 'meme',
aliases: ['alias_shared', 'alias_new'],
parent_tags: [parent_shared, parent_new],
created_by_user: member,
created_at: t_v2
)
end

let!(:other_v1) do
create_tag_version!(
tag: other_tag,
version_no: 1,
event_type: 'create',
name: 'other_tag_name',
category: 'general',
aliases: ['other_alias'],
parent_tags: [other_parent],
created_by_user: member,
created_at: t_other
)
end

describe 'GET /tags/versions' do
it 'returns all versions in reverse chronological order when id is omitted' do
get '/tags/versions'

expect(response).to have_http_status(:ok)
expect(json).to include('versions', 'count')
expect(json.fetch('count')).to eq(3)

versions = json.fetch('versions')

expect(versions.map { |v| [v['tag_id'], v['version_no']] }).to eq([
[other_tag.id, 1],
[tag.id, 2],
[tag.id, 1]
])
end

it 'returns versions for the specified tag with diffs' do
get '/tags/versions', params: { id: tag.id }

expect(response).to have_http_status(:ok)
expect(json).to include('versions', 'count')
expect(json.fetch('count')).to eq(2)

versions = json.fetch('versions')
expect(versions.map { |v| v['tag_id'] }.uniq).to eq([tag.id])
expect(versions.map { |v| v['version_no'] }).to eq([2, 1])

latest = versions.first
expect(latest).to include(
'tag_id' => tag.id,
'version_no' => 2,
'event_type' => 'update',
'created_by_user' => {
'id' => member.id,
'name' => member.name
}
)

expect(latest.fetch('name')).to eq(
'current' => 'new_tag_name',
'prev' => 'old_tag_name'
)
expect(latest.fetch('category')).to eq(
'current' => 'meme',
'prev' => 'general'
)
expect(latest.fetch('aliases')).to include(
{ 'name' => 'alias_shared', 'type' => 'context' },
{ 'name' => 'alias_new', 'type' => 'added' },
{ 'name' => 'alias_old', 'type' => 'removed' }
)
expect(latest.fetch('parent_tags')).to include(
a_hash_including(
'type' => 'context',
'tag' => a_hash_including(
'id' => parent_shared.id
)
),
a_hash_including(
'type' => 'added',
'tag' => a_hash_including(
'id' => parent_new.id
)
),
a_hash_including(
'type' => 'removed',
'tag' => a_hash_including(
'id' => parent_old.id
)
)
)
expect(latest.fetch('created_at')).to eq(t_v2.iso8601)

first = versions.second
expect(first).to include(
'tag_id' => tag.id,
'version_no' => 1,
'event_type' => 'create',
'created_by_user' => {
'id' => member.id,
'name' => member.name
}
)
expect(first.fetch('name')).to eq(
'current' => 'old_tag_name',
'prev' => nil
)
expect(first.fetch('category')).to eq(
'current' => 'general',
'prev' => nil
)
expect(first.fetch('aliases')).to include(
{ 'name' => 'alias_shared', 'type' => 'added' },
{ 'name' => 'alias_old', 'type' => 'added' }
)
expect(first.fetch('parent_tags')).to include(
a_hash_including(
'type' => 'added',
'tag' => a_hash_including(
'id' => parent_shared.id
)
),
a_hash_including(
'type' => 'added',
'tag' => a_hash_including(
'id' => parent_old.id
)
)
)
expect(first.fetch('created_at')).to eq(t_v1.iso8601)
end

it 'returns empty when the specified tag has no versions' do
fresh_tag = create(:tag, name: 'no_versions_tag', category: :general)

get '/tags/versions', params: { id: fresh_tag.id }

expect(response).to have_http_status(:ok)
expect(json.fetch('versions')).to eq([])
expect(json.fetch('count')).to eq(0)
end

it 'clamps page and limit to at least 1' do
get '/tags/versions', params: { id: tag.id, page: 0, limit: 0 }

expect(response).to have_http_status(:ok)
expect(json.fetch('count')).to eq(2)

versions = json.fetch('versions')
expect(versions.size).to eq(1)
expect(versions.first['version_no']).to eq(2)
end
end
end

+ 2
- 1
frontend/src/components/TopNav.tsx View File

@@ -47,7 +47,8 @@ export const menuOutline = ({ tag, wikiId, user, pathName }: {
{ name: `広場 (${ postCount || 0 })`,
to: `/posts?tags=${ encodeURIComponent (tag?.name ?? '') }`,
visible: tagFlg },
{ name: '履歴', to: `/tags/changes?id=${ tag?.id }`, visible: false }] },
{ name: '履歴', to: `/tags/changes?id=${ tag?.id }`,
visible: tagFlg && tag?.category !== 'nico' }] },
{ name: '素材', to: '/materials', visible: false, subMenu: [
{ name: '一覧', to: '/materials' },
{ name: '検索', to: '/materials/search', visible: false },


Loading…
Cancel
Save